<s> __doc__ = """<STR_LIT>""" <EOL> import vanilla <EOL> import GlyphsApp <EOL> import math <EOL> class MakeBubbleLayers ( object ) : <EOL> def __init__ ( self ) : <EOL> edY = <NUM_LIT> <EOL> txX = <NUM_LIT> <EOL> txY = <NUM_LIT> <EOL> spX = <NUM_LIT> <EOL> spY = <NUM_LIT:12> <EOL> btnX = <NUM_LIT> <EOL> btnY = <NUM_LIT:20> <EOL> windowWidth = <NUM_LIT> <EOL> windowHeight = <NUM_LIT> <EOL> windowWidthResize = <NUM_LIT:100> <EOL> windowHeightResize = <NUM_LIT:0> <EOL> self . w = vanilla . FloatingWindow ( <EOL> ( windowWidth , windowHeight ) , <EOL> "<STR_LIT>" , <EOL> autosaveName = "<STR_LIT>" <EOL> ) <EOL> self . w . textH = vanilla . TextBox ( ( spX , spY , txX , txY ) , "<STR_LIT>" ) <EOL> self . w . textV = vanilla . TextBox ( ( spX , spY * <NUM_LIT:2> + txY , txX , txY ) , "<STR_LIT>" ) <EOL> self . w . editH = vanilla . EditText ( ( - spX - <NUM_LIT> , spY , - spX , edY ) , "<STR_LIT>" ) <EOL> self . w . editV = vanilla . EditText ( ( - spX - <NUM_LIT> , spY * <NUM_LIT:2> + txY , - spX , edY ) , "<STR_LIT>" ) <EOL> self . w . line1 = vanilla . HorizontalLine ( ( <NUM_LIT:0> , spY * <NUM_LIT:3> + txY * <NUM_LIT:2> + <NUM_LIT:5> , - <NUM_LIT:0> , <NUM_LIT:1> ) ) <EOL> self . w . adhereToSB = vanilla . CheckBox ( ( spX , spY * <NUM_LIT:4> + txY * <NUM_LIT:2> , - spX , edY ) , "<STR_LIT>" , callback = self . checkBoxCallback , value = True ) <EOL> self . w . excess = vanilla . TextBox ( ( spX + <NUM_LIT:16> , spY * <NUM_LIT:5> + txY * <NUM_LIT:3> - <NUM_LIT:7> , - spX , txY ) , "<STR_LIT>" ) <EOL> self . w . excessRadio = vanilla . RadioGroup ( ( spX + <NUM_LIT:16> , spY * <NUM_LIT:6> + txY * <NUM_LIT:4> - <NUM_LIT> , <NUM_LIT> , edY ) , [ "<STR_LIT>" , "<STR_LIT>" ] , isVertical = False , ) <EOL> self . w . line2 = vanilla . HorizontalLine ( ( <NUM_LIT:0> , spY * <NUM_LIT:6> + txY * <NUM_LIT:5> - <NUM_LIT:4> , - <NUM_LIT:0> , <NUM_LIT:1> ) ) <EOL> self . w . textOverwrite = vanilla . TextBox ( ( spX , spY * <NUM_LIT:7> + txY * <NUM_LIT:5> - <NUM_LIT:8> , - spX , txY ) , "<STR_LIT>" ) <EOL> self . w . overwriteRadio = vanilla . RadioGroup ( ( spX , spY * <NUM_LIT:8> + txY * <NUM_LIT:6> - <NUM_LIT:11> , - spX , edY ) , [ "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" ] , isVertical = False , ) <EOL> self . w . line3 = vanilla . HorizontalLine ( ( <NUM_LIT:0> , spY * <NUM_LIT:9> + txY * <NUM_LIT:7> - <NUM_LIT:8> , - <NUM_LIT:0> , <NUM_LIT:1> ) ) <EOL> self . w . masterRadio = vanilla . RadioGroup ( ( spX , spY * <NUM_LIT:9> + txY * <NUM_LIT:7> , <NUM_LIT:0> , edY ) , [ "<STR_LIT>" , "<STR_LIT>" ] , isVertical = False , ) <EOL> self . w . runButton = vanilla . Button ( ( - <NUM_LIT> - <NUM_LIT:15> , - <NUM_LIT:20> - <NUM_LIT:15> , - <NUM_LIT:15> , - <NUM_LIT:15> ) , "<STR_LIT>" , sizeStyle = '<STR_LIT>' , callback = self . MakeBubbleLayersMain ) <EOL> self . w . setDefaultButton ( self . w . runButton ) <EOL> if not self . LoadPreferences ( ) : <EOL> self . w . excessRadio . set ( <NUM_LIT:0> ) <EOL> self . w . overwriteRadio . set ( <NUM_LIT:1> ) <EOL> self . w . masterRadio . set ( <NUM_LIT:1> ) <EOL> print "<STR_LIT>" <EOL> self . w . open ( ) <EOL> self . w . makeKey ( ) <EOL> def SavePreferences ( self , sender ) : <EOL> try : <EOL> Glyphs . defaults [ "<STR_LIT>" ] = self . w . editH . get ( ) <EOL> Glyphs . defaults [ "<STR_LIT>" ] = self . w . editV . get ( ) <EOL> Glyphs . defaults [ "<STR_LIT>" ] = self . w . adhereToSB . get ( ) <EOL> Glyphs . defaults [ "<STR_LIT>" ] = self . w . excessRadio . get ( ) <EOL> Glyphs . defaults [ "<STR_LIT>" ] = self . w . overwriteRadio . get ( ) <EOL> Glyphs . defaults [ "<STR_LIT>" ] = self . w . masterRadio . get ( ) <EOL> except : <EOL> return False <EOL> return True <EOL> def LoadPreferences ( self ) : <EOL> try : <EOL> self . w . editH . set ( Glyphs . defaults [ "<STR_LIT>" ] ) <EOL> self . w . editV . set ( Glyphs . defaults [ "<STR_LIT>" ] ) <EOL> self . w . adhereToSB . set ( Glyphs . defaults [ "<STR_LIT>" ] ) <EOL> self . w . excessRadio . set ( Glyphs . defaults [ "<STR_LIT>" ] ) <EOL> self . w . overwriteRadio . set ( Glyphs . defaults [ "<STR_LIT>" ] ) <EOL> self . w . masterRadio . set ( Glyphs . defaults [ "<STR_LIT>" ] ) <EOL> except : <EOL> return False <EOL> return True <EOL> def checkBoxCallback ( self , sender ) : <EOL> try : <EOL> if sender . get ( ) : <EOL> self . w . excessRadio . enable ( True ) <EOL> else : <EOL> self . w . excessRadio . enable ( False ) <EOL> except Exception , e : <EOL> Glyphs . showMacroWindow ( ) <EOL> print "<STR_LIT>" % e <EOL> def offsetPath ( self , Layer , h , v ) : <EOL> try : <EOL> GLYPHSAPPVERSION = NSBundle . bundleForClass_ ( GSMenu ) . infoDictionary ( ) . objectForKey_ ( "<STR_LIT>" ) <EOL> offsetCurveFilter = NSClassFromString ( "<STR_LIT>" ) <EOL> if GLYPHSAPPVERSION . startswith ( "<STR_LIT>" ) : <EOL> offsetCurveFilter . offsetLayer_offsetX_offsetY_makeStroke_position_error_shadow_ ( Layer , h , v , False , <NUM_LIT:0.5> , None , None ) <EOL> else : <EOL> offsetCurveFilter . offsetLayer_offsetX_offsetY_makeStroke_autoStroke_position_error_shadow_ ( Layer , h , v , False , False , <NUM_LIT:0.5> , None , None ) <EOL> except Exception , e : <EOL> Glyphs . showMacroWindow ( ) <EOL> print "<STR_LIT>" % e <EOL> def nudgeCurve ( self , oncurveMv , offcurve1 , offcurve2 , oncurveSt , offsetX ) : <EOL> try : <EOL> distanceX = oncurveMv . x - oncurveSt . x <EOL> distanceX1 = oncurveMv . x - offcurve1 . x <EOL> distanceX2 = offcurve2 . x - oncurveSt . x <EOL> if distanceX != <NUM_LIT:0> : <EOL> valueX1 = distanceX1 / distanceX <EOL> valueX2 = distanceX2 / distanceX <EOL> else : <EOL> valueX1 = <NUM_LIT:0> <EOL> valueX2 = <NUM_LIT:0> <EOL> if distanceX1 != <NUM_LIT:0> : <EOL> offcurve1 . x += ( <NUM_LIT:1> - valueX1 ) * offsetX <EOL> else : <EOL> offcurve1 . x += offsetX <EOL> if distanceX2 != <NUM_LIT:0> : <EOL> offcurve2 . x += ( valueX2 ) * offsetX <EOL> except Exception , e : <EOL> Glyphs . showMacroWindow ( ) <EOL> print "<STR_LIT>" % e <EOL> def fitToSidebearing ( self , givenLayer , givenMaster ) : <EOL> try : <EOL> nudgeExcess = True if self . w . excessRadio . get ( ) == <NUM_LIT:0> else False <EOL> boundL = givenLayer . bounds . origin . x <EOL> boundR = boundL + givenLayer . bounds . size . width <EOL> for thisPath in givenLayer . paths : <EOL> numOfNodes = len ( thisPath . nodes ) <EOL> for i in range ( numOfNodes ) : <EOL> node = thisPath . nodes [ i ] <EOL> interesting = False <EOL> if node . type == <NUM_LIT:1> or node . type == <NUM_LIT> : <EOL> if node . x < <NUM_LIT:0> and nudgeExcess : <EOL> interesting = True <EOL> offsetX = - node . x <EOL> elif ( <NUM_LIT:0> < node . x ) and ( node . x == boundL ) : <EOL> interesting = True <EOL> offsetX = - node . x <EOL> elif ( node . x == boundR ) and ( node . x < givenLayer . width ) : <EOL> interesting = True <EOL> offsetX = givenLayer . width - node . x <EOL> elif givenLayer . width < node . x and nudgeExcess : <EOL> interesting = True <EOL> offsetX = givenLayer . width - node . x <EOL> if interesting : <EOL> nodeBefore = thisPath . nodes [ i - <NUM_LIT:1> ] <EOL> if nodeBefore . type == <NUM_LIT> : <EOL> if thisPath . nodes [ i - <NUM_LIT:2> ] . type == <NUM_LIT> : <EOL> oncurveMv = node <EOL> offcurve1 = nodeBefore <EOL> offcurve2 = thisPath . nodes [ i - <NUM_LIT:2> ] <EOL> oncurveSt = thisPath . nodes [ i - <NUM_LIT:3> ] <EOL> self . nudgeCurve ( oncurveMv , offcurve1 , offcurve2 , oncurveSt , offsetX ) <EOL> nodeAfter = thisPath . nodes [ i + <NUM_LIT:1> ] <EOL> if nodeAfter . type == <NUM_LIT> : <EOL> if thisPath . nodes [ i + <NUM_LIT:2> ] . type == <NUM_LIT> : <EOL> oncurveMv = node <EOL> offcurve1 = nodeAfter <EOL> offcurve2 = thisPath . nodes [ i + <NUM_LIT:2> ] <EOL> oncurveSt = thisPath . nodes [ i + <NUM_LIT:3> ] <EOL> self . nudgeCurve ( oncurveMv , offcurve1 , offcurve2 , oncurveSt , offsetX ) <EOL> node . x += offsetX <EOL> if nudgeExcess == False : <EOL> wi = givenLayer . width <EOL> slant = givenMaster . italicAngle <EOL> slantOrigin = givenMaster . xHeight / <NUM_LIT:2> <EOL> if slant == <NUM_LIT:0> : <EOL> ( difBottom , difTop ) = <NUM_LIT:0> , <NUM_LIT:0> <EOL> else : <EOL> difTop = math . tan ( math . radians ( slant ) ) * ( <NUM_LIT> - slantOrigin ) <EOL> difBottom = - ( math . tan ( math . radians ( slant ) ) * ( slantOrigin + <NUM_LIT> ) ) <EOL> eraserRectNodes = [ <EOL> [ [ - <NUM_LIT> + difBottom , - <NUM_LIT> ] , [ <NUM_LIT:0> + difBottom , - <NUM_LIT> ] , [ <NUM_LIT:0> + difTop , <NUM_LIT> ] , [ - <NUM_LIT> + difTop , <NUM_LIT> ] ] , <EOL> [ [ wi + difBottom , - <NUM_LIT> ] , [ wi + <NUM_LIT> + difBottom , - <NUM_LIT> ] , [ wi + <NUM_LIT> + difTop , <NUM_LIT> ] , [ wi + difTop , <NUM_LIT> ] ] <EOL> ] <EOL> Erasers = NSMutableArray . alloc ( ) . init ( ) <EOL> for eraser in eraserRectNodes : <EOL> eraserRect = GSPath ( ) <EOL> for node in eraser : <EOL> newNode = GSNode ( ) <EOL> newNode . type = GSLINE <EOL> newNode . position = ( node [ <NUM_LIT:0> ] , node [ <NUM_LIT:1> ] ) <EOL> eraserRect . nodes . append ( newNode ) <EOL> eraserRect . closed = True <EOL> Erasers . append ( eraserRect ) <EOL> PathOperator = GSPathOperator . alloc ( ) . init ( ) <EOL> Paths = givenLayer . pyobjc_instanceMethods . paths ( ) <EOL> PathOperator . subtractPaths_from_error_ ( Erasers , Paths , None ) <EOL> givenLayer . paths = Paths <EOL> except Exception , e : <EOL> Glyphs . showMacroWindow ( ) <EOL> print "<STR_LIT>" % e <EOL> def MakeBubbleLayersMain ( self , sender ) : <EOL> try : <EOL> font = Glyphs . font <EOL> fontMaster = font . selectedFontMaster <EOL> selectedLayers = font . selectedLayers <EOL> GLYPHSAPPVERSION = NSBundle . bundleForClass_ ( GSMenu ) . infoDictionary ( ) . objectForKey_ ( "<STR_LIT>" ) <EOL> font . disableUpdateInterface ( ) <EOL> try : <EOL> offsetH = int ( self . w . editH . get ( ) ) <EOL> offsetV = int ( self . w . editV . get ( ) ) <EOL> except : <EOL> Glyphs . displayDialog_withTitle_ ( "<STR_LIT>" , "<STR_LIT>" ) <EOL> if self . w . masterRadio . get ( ) == <NUM_LIT:0> : <EOL> masters = [ font . selectedFontMaster ] <EOL> else : <EOL> masters = font . masters <EOL> for gLayer in selectedLayers : <EOL> glyph = gLayer . parent <EOL> glyph . beginUndo ( ) <EOL> if self . w . overwriteRadio . get ( ) == <NUM_LIT:0> : <EOL> bubbleList = [ layer . associatedFontMaster ( ) for layer in glyph . layers if layer . name == '<STR_LIT>' ] <EOL> elif self . w . overwriteRadio . get ( ) == <NUM_LIT:1> : <EOL> bubbleList = [ layer . associatedFontMaster ( ) for layer in glyph . layers if layer . name == '<STR_LIT>' and len ( layer . paths ) > <NUM_LIT:0> ] <EOL> bubblesToBeDeleted = [ layer for layer in glyph . layers if layer . name == '<STR_LIT>' and len ( layer . paths ) == <NUM_LIT:0> ] <EOL> else : <EOL> bubbleList = [ ] <EOL> bubblesToBeDeleted = [ layer for layer in glyph . layers if layer . name == '<STR_LIT>' ] <EOL> if '<STR_LIT>' in locals ( ) : <EOL> for emptyBubble in bubblesToBeDeleted : <EOL> glyph . removeLayerForKey_ ( emptyBubble . layerId ) <EOL> for master in masters : <EOL> if not master in bubbleList : <EOL> newBubbleLayer = GSLayer ( ) <EOL> newBubbleLayer . name = "<STR_LIT>" <EOL> newBubbleLayer . width = glyph . layers [ master . id ] . width <EOL> newBubbleLayer . associatedMasterId = master . id <EOL> for pathToCopy in glyph . layers [ master . id ] . paths : <EOL> newBubbleLayer . addPath_ ( pathToCopy . copy ( ) ) <EOL> self . offsetPath ( newBubbleLayer , offsetH , offsetV ) <EOL> newBubbleLayer . removeOverlap ( ) <EOL> for path in newBubbleLayer . paths : <EOL> if path . direction == <NUM_LIT:1> : <EOL> path . reverse ( ) <EOL> newBubbleLayer . removeOverlap ( ) <EOL> if self . w . adhereToSB . get ( ) : <EOL> self . fitToSidebearing ( newBubbleLayer , master ) <EOL> glyph . layers . append ( newBubbleLayer ) <EOL> glyph . endUndo ( ) <EOL> font . enableUpdateInterface ( ) <EOL> if not self . SavePreferences ( self ) : <EOL> print "<STR_LIT>" <EOL> except Exception , e : <EOL> Glyphs . showMacroWindow ( ) <EOL> print "<STR_LIT>" % e <EOL> MakeBubbleLayers ( ) </s>
