class Rotator(BaseWindowController): _title = 'Rotator' _width = 180 _frame = 8 _height = 249 _row = 24 _padding = 16 _gutter = 8 _lineHeight = 20 _color = NSColor.colorWithCalibratedRed_green_blue_alpha_( 0.0, 0.5, 1.0, .8) _columns = 3 _col_width = (_width - ((_columns - 1) * _gutter)) / _columns _col_0 = _frame _col_1 = _frame + _col_width + _gutter _col_2 = _frame + 2 * _col_width + 2 * _gutter _col_3 = _frame + 3 * _col_width + 3 * _gutter xValue = getExtensionDefault('%s.%s' % (rotatorDefaults, 'x'), 0) yValue = getExtensionDefault('%s.%s' % (rotatorDefaults, 'y'), 0) steps = getExtensionDefault('%s.%s' % (rotatorDefaults, 'steps'), 12) lock = getExtensionDefault('%s.%s' % (rotatorDefaults, 'lock'), False) rounding = getExtensionDefault('%s.%s' % (rotatorDefaults, 'round'), False) angle = 360.0 / steps def __init__(self): self.w = FloatingWindow((self._width + 2 * self._frame, self._height), self._title) # ---------- # text boxes # ---------- textBoxY = self._padding self.w.steps_label = TextBox( (self._col_0, textBoxY, self._col_width, self._lineHeight), 'Steps', alignment='right') if rfVersion >= 3.4: self.w.steps_text = NumberEditText( (self._col_1, textBoxY - 2, self._col_width, self._lineHeight), self.steps, callback=self.angleCallback, allowFloat=False, allowNegative=False, allowEmpty=False, minimum=1, decimals=0, continuous=True) else: self.w.steps_text = EditText( (self._col_1, textBoxY - 2, self._col_width, self._lineHeight), self.steps, callback=self.angleCallback, continuous=True) textBoxY += (self._row) self.w.xValue_label = TextBox( (self._col_0, textBoxY, self._col_width, self._lineHeight), 'x', alignment='right') if rfVersion >= 3.4: self.w.xValue_text = NumberEditText( (self._col_1, textBoxY - 2, self._col_width, self._lineHeight), self.xValue, callback=self.xCallback, allowFloat=True, decimals=0) else: self.w.xValue_text = EditText( (self._col_1, textBoxY - 2, self._col_width, self._lineHeight), self.xValue, callback=self.xCallback) textBoxY += (self._row) self.w.yValue_label = TextBox( (self._col_0, textBoxY, self._col_width, self._lineHeight), 'y', alignment='right') if rfVersion >= 3.4: self.w.yValue_text = NumberEditText( (self._col_1, textBoxY - 2, self._col_width, self._lineHeight), self.yValue, callback=self.yCallback, allowFloat=True, decimals=0) else: self.w.yValue_text = EditText( (self._col_1, textBoxY - 2, self._col_width, self._lineHeight), self.yValue, callback=self.yCallback) textBoxY += (self._row) self.w.angle_label = TextBox( (self._col_0, textBoxY, self._col_width, self._lineHeight), 'Angle', alignment='right') self.w.angleResult = TextBox( (self._col_1, textBoxY, self._col_width, self._lineHeight), u'%s°' % self.niceAngleString(self.angle)) textBoxY += (self._row) textBoxY += (self._row * .25) self.w.line = HorizontalLine( (self._gutter, textBoxY, -self._gutter, 0.5)) textBoxY += (self._row * .25) self.w.lock_checkbox = CheckBox( (self._col_1 - 25, textBoxY, -self._gutter, self._lineHeight), 'Lock Center', value=self.lock, callback=self.lockCallback) textBoxY += (self._row) self.w.rounding_checkbox = CheckBox( (self._col_1 - 25, textBoxY, -self._gutter, self._lineHeight), 'Round Result', value=self.rounding, callback=self.roundingCallback) textBoxY += (self._row) # ------- # buttons # ------- self.w.color = ColorWell( (self._col_0, textBoxY, -self._gutter, 2 * self._lineHeight), color=getExtensionDefaultColor( '%s.%s' % (rotatorDefaults, 'color'), self._color), callback=self.colorCallback) textBoxY += (self._row) self.w.buttonRotate = Button( (self._col_0, -30, -self._gutter, self._lineHeight), 'Rotate', callback=self.rotateCallback) self.setUpBaseWindowBehavior() addObserver(self, 'updateOrigin', 'mouseDragged') addObserver(self, 'drawRotationPreview', 'drawBackground') addObserver(self, 'drawSolidPreview', 'drawPreview') self.w.setDefaultButton(self.w.buttonRotate) self.w.open() def drawRotationPreview(self, info): # draw preview glyph outline = self.getRotatedGlyph() pen = CocoaPen(None) self.w.color.get().set() outline.draw(pen) pen.path.setLineWidth_(info['scale'] * .5) pen.path.stroke() # draw crosshair ch_pen = CocoaPen(None) center_x = self.xValue center_y = self.yValue strokeColor = NSColor.redColor() strokeColor.set() ch_pen.moveTo((center_x - 10, center_y)) ch_pen.lineTo((center_x + 10, center_y)) ch_pen.endPath() ch_pen.moveTo((center_x, center_y - 10)) ch_pen.lineTo((center_x, center_y + 10)) ch_pen.endPath() ch_pen.path.setLineWidth_(info['scale']) ch_pen.path.stroke() def drawSolidPreview(self, info): outline = self.getRotatedGlyph() pen = CocoaPen(None) outline.draw(pen) defaultPreviewColor = getDefault('glyphViewPreviewFillColor') fillColor = NSColor.colorWithCalibratedRed_green_blue_alpha_( *defaultPreviewColor) fillColor.set() pen.path.fill() def xCallback(self, sender): xValue = sender.get() try: self.xValue = int(xValue) except ValueError: xValue = self.xValue self.w.xValue_text.set(xValue) UpdateCurrentGlyphView() def yCallback(self, sender): yValue = sender.get() try: self.yValue = int(yValue) except ValueError: yValue = self.yValue self.w.yValue_text.set(yValue) UpdateCurrentGlyphView() def lockCallback(self, sender): self.lock = not self.lock self.saveDefaults() def roundingCallback(self, sender): self.rounding = not self.rounding self.saveDefaults() def angleCallback(self, sender): try: stepValue = float(sender.get()) stepValue = int(round(stepValue)) except ValueError: stepValue = self.steps self.w.steps_text.set(self.steps) self.steps = stepValue if abs(stepValue) < 2: self.angle = 90.0 elif stepValue == 0: self.angle = 0 else: self.angle = 360 / stepValue self.w.angleResult.set(u'%s°' % self.niceAngleString(self.angle)) UpdateCurrentGlyphView() def niceAngleString(self, angle): angleResultString = u'%.2f' % angle if angleResultString.endswith('.00'): angleResultString = angleResultString[0:-3] return angleResultString def colorCallback(self, sender): setExtensionDefaultColor('%s.%s' % (rotatorDefaults, 'color'), sender.get()) UpdateCurrentGlyphView() def windowCloseCallback(self, sender): removeObserver(self, 'mouseUp') removeObserver(self, 'drawBackground') removeObserver(self, 'drawPreview') UpdateCurrentGlyphView() self.saveDefaults() def updateOrigin(self, info): if not self.lock: self.xValue, self.yValue = int(round(info['point'].x)), int( round(info['point'].y)) self.w.xValue_text.set(self.xValue) self.w.yValue_text.set(self.yValue) def saveDefaults(self): setExtensionDefault('%s.%s' % (rotatorDefaults, 'x'), self.xValue) setExtensionDefault('%s.%s' % (rotatorDefaults, 'y'), self.yValue) setExtensionDefault('%s.%s' % (rotatorDefaults, 'steps'), self.steps) setExtensionDefault('%s.%s' % (rotatorDefaults, 'lock'), self.lock) setExtensionDefault('%s.%s' % (rotatorDefaults, 'round'), self.rounding) def rotateCallback(self, sender): glyph = CurrentGlyph() glyph.prepareUndo('Rotator') rotatedGlyph = self.getRotatedGlyph() glyph.appendGlyph(rotatedGlyph) glyph.performUndo() self.saveDefaults() glyph.changed() def getRotatedGlyph(self): glyph = CurrentGlyph() x = int(self.w.xValue_text.get()) y = int(self.w.yValue_text.get()) steps = self.steps angle = self.angle center = (x, y) rotation_result_glyph = RGlyph() rotation_step_glyph = RGlyph() pen = rotation_step_glyph.getPointPen() contourList = [] for idx, contour in enumerate(glyph): if contour.selected: contourList.append(idx) # if nothing is selected, the whole glyph will be rotated. if len(contourList) == 0: for idx, contour in enumerate(glyph): contourList.append(idx) for contour in contourList: glyph[contour].drawPoints(pen) # don't draw the original shape again stepCount = steps - 1 if steps < 2: # solution/hack for 1-step rotation stepCount = 1 angle = 90 for i in range(stepCount): rotation_step_glyph.rotateBy(angle, center) rotation_result_glyph.appendGlyph(rotation_step_glyph) if self.rounding: rotation_result_glyph.round() return rotation_result_glyph
class Adhesiontext(BaseWindowController): def __init__(self): flushAlign = 76 firstRowY = 12 rowOffsetY = 30 firstCheckY = 135 checkOffsetY = 27 rightMarginX = -12 self.windowWidth = 410 self.windowHeightWithoutOptions = 45 self.windowHeightWithOptions = 280 self.scriptIsRTL = False windowPos = getExtensionDefault("%s.%s" % (extensionKey, "windowPos")) if not windowPos: windowPos = (100, 100) self.optionsVisible = getExtensionDefault("%s.%s" % (extensionKey, "optionsVisible")) if self.optionsVisible: optionsButtonSign = '-' windowHeight = self.windowHeightWithOptions else: self.optionsVisible = False # needs to be set because the first time the extension runs self.optionsVisible will be None optionsButtonSign = '+' windowHeight = self.windowHeightWithoutOptions self.chars = getExtensionDefault("%s.%s" % (extensionKey, "chars")) if not self.chars: self.chars = '' self.sliderValue = getExtensionDefault("%s.%s" % (extensionKey, "sliderValue")) if not self.sliderValue: self.sliderValue = 25 self.scriptsIndex = getExtensionDefault("%s.%s" % (extensionKey, "scriptsIndex")) if not self.scriptsIndex: self.scriptsIndex = 0 self.langsIndex = getExtensionDefault("%s.%s" % (extensionKey, "langsIndex")) if not self.langsIndex: self.langsIndex = 0 self.w = FloatingWindow((windowPos[0], windowPos[1], self.windowWidth, windowHeight), "adhesiontext") # 1st row self.w.labelChars = TextBox((10, firstRowY, flushAlign, 20), "Characters:", alignment="right") self.w.chars = EditText((flushAlign +15, firstRowY -1, 199, 22), self.chars, callback=self.charsCallback) self.w.button = Button((300, firstRowY, 68, 20), "Get text", callback=self.buttonCallback) self.w.spinner = FixedSpinner((325, firstRowY, 20, 20), displayWhenStopped=False) self.w.optionsButton = SquareButton((378, firstRowY +1, 18, 18), optionsButtonSign, sizeStyle="small", callback=self.optionsCallback) # set the initial state of the button according to the content of the chars EditText if len(self.w.chars.get()): self.w.button.enable(True) else: self.w.button.enable(False) # keep track of the content of chars EditText self.previousChars = self.w.chars.get() # 2nd row self.w.labelWords = TextBox((10, firstRowY + rowOffsetY, flushAlign, 20), "Words:", alignment="right") self.w.wordCount = TextBox((flushAlign +12, firstRowY + rowOffsetY, 40, 20), alignment="left") self.w.slider = Slider((flushAlign +47, firstRowY + rowOffsetY +1, 165, 20), value=self.sliderValue, minValue=5, maxValue=200, callback=self.sliderCallback) # set the initial wordCount value according to the position of the slider self.w.wordCount.set(int(self.w.slider.get())) # 3rd row self.w.labelScripts = TextBox((10, firstRowY + rowOffsetY *2, flushAlign, 20), "Script:", alignment="right") self.w.scriptsPopup = PopUpButton((flushAlign +15, firstRowY + rowOffsetY *2, 150, 20), scriptsNameList, callback=self.scriptsCallback) self.w.scriptsPopup.set(self.scriptsIndex) # 4th row self.w.labelLangs = TextBox((10, firstRowY + rowOffsetY *3, flushAlign, 20), "Language:", alignment="right") self.w.langsPopup = PopUpButton((flushAlign +15, firstRowY + rowOffsetY *3, 150, 20), []) # set the initial list of languages according to the script value self.w.langsPopup.setItems(langsNameDict[scriptsNameList[self.w.scriptsPopup.get()]]) self.w.langsPopup.set(self.langsIndex) self.punctCheck = getExtensionDefault("%s.%s" % (extensionKey, "punctCheck")) if not self.punctCheck: self.punctCheck = 0 self.figsCheck = getExtensionDefault("%s.%s" % (extensionKey, "figsCheck")) if not self.figsCheck: self.figsCheck = 0 self.figsPopup = getExtensionDefault("%s.%s" % (extensionKey, "figsPopup")) if not self.figsPopup: self.figsPopup = 0 self.trimCheck = getExtensionDefault("%s.%s" % (extensionKey, "trimCheck")) if not self.trimCheck: self.trimCheck = 0 self.caseCheck = getExtensionDefault("%s.%s" % (extensionKey, "caseCheck")) if not self.caseCheck: self.caseCheck = 0 self.casingCheck = getExtensionDefault("%s.%s" % (extensionKey, "casingCheck")) if not self.casingCheck: self.casingCheck = 0 self.casingPopup = getExtensionDefault("%s.%s" % (extensionKey, "casingPopup")) if not self.casingPopup: self.casingPopup = 0 # 1st checkbox self.w.punctCheck = CheckBox((flushAlign +15, firstCheckY, 130, 20), "Add punctuation") self.w.punctCheck.set(self.punctCheck) # 2nd checkbox self.w.figsCheck = CheckBox((flushAlign +15, firstCheckY + checkOffsetY, 120, 20), "Insert numbers", callback=self.figsCallback) self.w.figsCheck.set(self.figsCheck) self.w.figsPopup = PopUpButton((210, firstCheckY + checkOffsetY, 90, 20), figOptionsList) self.w.figsPopup.set(self.figsPopup) # enable or disable the figure options PopUp depending on the figures CheckBox if scriptsNameList[self.w.scriptsPopup.get()] in enableFigOptionList: self.w.figsPopup.show(True) if self.w.figsCheck.get(): self.w.figsPopup.enable(True) else: self.w.figsPopup.enable(False) else: self.w.figsPopup.show(False) # 3rd checkbox self.w.trimCheck = CheckBoxPlus((flushAlign +15, firstCheckY + checkOffsetY *2, 120, 20), "Trim accents") self.w.trimCheck.set(self.trimCheck) if scriptsNameList[self.w.scriptsPopup.get()] in enableTrimCheckList: self.w.trimCheck.enable(True) else: self.w.trimCheck.enable(False) # 4th checkbox self.w.caseCheck = CheckBoxPlus((flushAlign +15, firstCheckY + checkOffsetY *3, 120, 20), "Ignore casing") self.w.caseCheck.set(self.caseCheck) if scriptsNameList[self.w.scriptsPopup.get()] in enableCaseCheckList: self.w.caseCheck.enable(True) else: self.w.caseCheck.enable(False) # 5th checkbox self.w.casingCheck = CheckBoxPlus((flushAlign +15, firstCheckY + checkOffsetY *4, 115, 20), "Change casing", callback=self.casingCallback) self.w.casingCheck.set(self.casingCheck) if scriptsNameList[self.w.scriptsPopup.get()] in enableCaseCheckList: self.w.casingCheck.enable(True) else: self.w.casingCheck.enable(False) self.w.casingPopup = PopUpButton((210, firstCheckY + checkOffsetY *4, 90, 20), casingNameList) self.w.casingPopup.set(self.casingPopup) # enable or disable the casing PopUp depending on the casing CheckBox if self.w.casingCheck.get() and self.w.casingCheck.isEnable(): self.w.casingPopup.enable(True) else: self.w.casingPopup.enable(False) self.nsTextField = self.w.chars.getNSTextField() self.w.setDefaultButton(self.w.button) self.w.bind("close", self.windowClose) self.w.open() self.w.makeKey() def windowClose(self, sender): self.saveExtensionDefaults() def saveExtensionDefaults(self): setExtensionDefault("%s.%s" % (extensionKey, "windowPos"), self.w.getPosSize()[0:2]) setExtensionDefault("%s.%s" % (extensionKey, "optionsVisible"), self.optionsVisible) setExtensionDefault("%s.%s" % (extensionKey, "chars"), self.w.chars.get()) setExtensionDefault("%s.%s" % (extensionKey, "sliderValue"), int(self.w.slider.get())) setExtensionDefault("%s.%s" % (extensionKey, "scriptsIndex"), int(self.w.scriptsPopup.get())) setExtensionDefault("%s.%s" % (extensionKey, "langsIndex"), int(self.w.langsPopup.get())) setExtensionDefault("%s.%s" % (extensionKey, "punctCheck"), self.w.punctCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "figsCheck"), self.w.figsCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "figsPopup"), self.w.figsPopup.get()) setExtensionDefault("%s.%s" % (extensionKey, "trimCheck"), self.w.trimCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "caseCheck"), self.w.caseCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "casingCheck"), self.w.casingCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "casingPopup"), self.w.casingPopup.get()) def buttonCallback(self, sender): sender.enable(False) self.w.spinner.start() self.getText() self.w.spinner.stop() sender.enable(True) def optionsCallback(self, sender): sign = sender.getTitle() if sign == "+": sender.setTitle("-") self.w.resize(self.windowWidth, self.windowHeightWithOptions, animate=True) self.optionsVisible = True else: sender.setTitle("+") self.w.resize(self.windowWidth, self.windowHeightWithoutOptions, animate=True) self.optionsVisible = False def charsCallback(self, sender): charsContent = sender.get() if len(charsContent): self.w.button.enable(True) nsTextView = self.nsTextField.currentEditor() # NOTE: the field editor is only available when NSTextField is in editing mode. # when only one glyph is selected and copied, the contents of the clipboard are the glyph's XML # instead of its unicode character or its name; therefore, post-process the pasted content. if xmlHeader in charsContent: caretIndex = charsContent.index(xmlHeader) codepointString = re_glyphUnicode.search(charsContent) glyphName = re_glyphName.search(charsContent) if codepointString: replacement = unichr(eval('0x' + codepointString.group(1))) elif glyphName: replacement = '/' + glyphName.group(1) else: replacement = '' # replace the glyph's XML by its unicode character or its name self.w.chars.set(re_glyph.sub(replacement, charsContent)) # restore the location of the caret location = caretIndex + len(replacement) nsTextView.setSelectedRange_((location, 0)) # update the variable charsContent = sender.get() caretIndex = nsTextView.selectedRanges()[0].rangeValue().location # Limit the number of characters numeralWasFound = self.stringHasNumeral(charsContent) if len(charsContent) > maxChars or numeralWasFound: NSBeep() if numeralWasFound: self.showMessage("Sorry, numerals are not allowed.", "") else: self.showMessage("You've reached the maximum \rnumber of characters.", "The limit is %d." % maxChars) # restore the content of chars EditText to the previous string sender.set(self.previousChars) # restore the focus on the chars EditText and restore the location of the caret caretIndexAdjust = len(self.previousChars) - len(charsContent) self.w.getNSWindow().makeFirstResponder_(self.nsTextField) nsTextView.setSelectedRange_((caretIndex + caretIndexAdjust, 0)) # update the stored string self.previousChars = sender.get() else: self.w.button.enable(False) def sliderCallback(self, sender): self.w.wordCount.set(int(sender.get())) def scriptsCallback(self, sender): self.w.langsPopup.setItems(langsNameDict[scriptsNameList[sender.get()]]) # toggle RTL/LTR if scriptsNameList[sender.get()] in rightToLeftList: self.scriptIsRTL = True self.nsTextField.setBaseWritingDirection_(NSWritingDirectionRightToLeft) self.nsTextField.setAlignment_(NSRightTextAlignment) else: self.scriptIsRTL = False self.nsTextField.setBaseWritingDirection_(NSWritingDirectionLeftToRight) self.nsTextField.setAlignment_(NSLeftTextAlignment) # restore the focus on the chars EditText self.w.getNSWindow().makeFirstResponder_(self.nsTextField) # toggle figsPopup if scriptsNameList[sender.get()] in enableFigOptionList: self.w.figsPopup.show(True) if self.w.figsCheck.get(): self.w.figsPopup.enable(True) else: self.w.figsPopup.enable(False) else: self.w.figsPopup.show(False) # toggle trimCheck if scriptsNameList[sender.get()] in enableTrimCheckList: self.w.trimCheck.enable(True) else: self.w.trimCheck.enable(False) # toggle caseCheck and casingCheck if scriptsNameList[sender.get()] in enableCaseCheckList: self.w.caseCheck.enable(True) self.w.casingCheck.enable(True) if self.w.casingCheck.get(): self.w.casingPopup.enable(True) else: self.w.caseCheck.enable(False) self.w.casingCheck.enable(False) self.w.casingPopup.enable(False) def figsCallback(self, sender): if sender.get(): self.w.figsPopup.enable(True) else: self.w.figsPopup.enable(False) def casingCallback(self, sender): if sender.get(): self.w.casingPopup.enable(True) else: self.w.casingPopup.enable(False) def stringHasNumeral(self, string): if re_numeral.search(string): return True return False def isConnected(self): try: urlopen(url, timeout=3) return True except URLError: pass return False def getText(self): if CurrentFont() is None: NSBeep() self.showMessage("Open a font first.", "") return if not self.isConnected(): NSBeep() self.showMessage("Required internet connection not found.", "") return values = {'chars' : self.w.chars.get().encode('utf-8'), 'script' : scriptsTagDict[scriptsNameList[self.w.scriptsPopup.get()]], 'tb' : langsTagDict[langsNameDict[scriptsNameList[self.w.scriptsPopup.get()]][self.w.langsPopup.get()]] } if self.w.punctCheck.get(): values['punct'] = True if self.w.figsCheck.get(): values['figs'] = True if self.w.figsPopup.isVisible(): figsOptTagsList = ["dflt", "locl"] values['figsOpt'] = figsOptTagsList[self.w.figsPopup.get()] if self.w.trimCheck.get() and self.w.trimCheck.isEnable(): values['trim'] = True if self.w.caseCheck.get() and self.w.caseCheck.isEnable(): values['case'] = True if self.w.casingCheck.get() and self.w.casingCheck.isEnable(): values['casing'] = casingNameList[self.w.casingPopup.get()].lower() data = urlencode(values) data = data.encode('utf-8') print(data) request = Request(url, data) response = urlopen(request) text = response.read() textU = unicode(text, 'utf-8') if (msgStr in textU): textU = textU.replace(msgStr, "") NSBeep() self.showMessage(textU, "") return elif (wrnStr in textU): resultIndex = textU.find(rsltStr) secmsgIndex = textU.find(sndStr) frstmsgU = textU[:secmsgIndex].replace(wrnStr, "") scndmsgU = textU[secmsgIndex:resultIndex].replace(sndStr, "") textU = textU[resultIndex:].replace(rsltStr, "") NSBeep() self.showMessage(frstmsgU, scndmsgU) textList = textU.split() trimmedText = ' '.join(textList[:int(self.w.slider.get())]) if CurrentSpaceCenter() is None: OpenSpaceCenter(CurrentFont(), newWindow=False) sp = CurrentSpaceCenter() print(trimmedText) sp.setRaw(trimmedText) # Toggle RTL-LTR try: sp.setLeftToRight(not self.scriptIsRTL) sp.setInputWritingDirection('Right to Left' if self.scriptIsRTL else 'Left to Right') except AttributeError: pass return
class Script(object): def __init__(self): self.valuesPrefsKey = prefsKey + ".delta." + basename( Glyphs.font.filepath) # UI metrics spacing = 8 height = 22 # calculate window height minWinSize = (220, 120 + (len(Glyphs.font.masters) * (height + spacing))) # create UI window self.w = FloatingWindow(minWinSize, "Adjust sidebearings", minSize=minWinSize, maxSize=(500, 500), autosaveName=prefsKey + ".win") # layout UI controls y = 16 self.w.label = TextBox((16, y, -16, height), "Sidebearing delta adjustment:") y += height + spacing inputWidth = 64 for master in Glyphs.font.masters: setattr(self.w, "deltaLabel%s" % master.id, TextBox((16, y, -16 - inputWidth, height), master.name)) setattr(self.w, "deltaInput%s" % master.id, EditText((-16 - inputWidth, y, -16, height), "16")) # print("self.w.deltaInputs[master.id]", getattr(self.w, "deltaInput%s" % master.id)) y += height + spacing self.w.submitButton = Button((16, -16 - height, -16, height), "Adjust all sidebearings", callback=self.onSubmit) # finalize UI self.w.setDefaultButton(self.w.submitButton) self.loadPreferences() self.w.bind("close", self.savePreferences) # make sure window is large enough to show all masters x, y, w, h = self.w.getPosSize() if w < minWinSize[0] and h < minWinSize[1]: self.w.setPosSize((x, y, minWinSize[0], minWinSize[1]), animate=False) elif w < minWinSize[0]: self.w.setPosSize((x, y, minWinSize[0], h), animate=False) elif h < minWinSize[1]: self.w.setPosSize((x, y, w, minWinSize[1]), animate=False) self.w.open() self.w.makeKey() def getDeltaInputForMaster(self, masterId): return getattr(self.w, "deltaInput%s" % masterId) def loadPreferences(self): try: Glyphs.registerDefault(self.valuesPrefsKey, []) for t in Glyphs.defaults[self.valuesPrefsKey]: try: masterId, value = t self.getDeltaInputForMaster(masterId).set(value) except: pass except: print("failed to load preferences") def savePreferences(self, sender): try: values = [] for master in Glyphs.font.masters: values.append( (master.id, self.getDeltaInputForMaster(master.id).get())) Glyphs.defaults[self.valuesPrefsKey] = values except: print("failed to save preferences") def onSubmit(self, sender): try: sender.enable(False) if performFontChanges(self.action1): self.w.close() except Exception, e: Glyphs.showMacroWindow() print("error: %s" % e) finally:
class ProofGroupInspector: def __init__(self, proofGroup): """ Initialize inspector with proofGroup data. proofGroup is a dictionary passed in by ProofDrawer(). """ self.proofGroup = proofGroup self.editedProofGroup = {} left = 10 row = 10 textboxWidth = 92 leftEditText = left + 95 pointSizes = ["6", "8", "10", "12", "14", "18",\ "21", "24", "36", "48", "60", "72"] self.w = FloatingWindow( (400, 275), "Edit Proof Group: %s" % self.proofGroup["name"]) self.w.groupName = TextBox((left, row + 2, textboxWidth, 20), "Group name:", alignment="right") self.w.groupNameEdit = EditText((leftEditText, row, -10, 22), self.proofGroup["name"]) row += 33 self.w.typeSize = TextBox((left, row + 2, textboxWidth, 20), "Type size (pt):", alignment="right") self.w.typeSizeEdit = ComboBox((leftEditText, row, 55, 22), pointSizes, continuous=True, callback=self._checkFloat) self.w.typeSizeEdit.set(self.proofGroup["typeSize"]) self.w.leading = TextBox((leftEditText + 80, row + 2, 60, 22), "Leading:") self.w.leadingEdit = ComboBox((leftEditText + 139, row, 55, 22), pointSizes, continuous=True, callback=self._checkFloat) self.w.leadingEdit.set(self.proofGroup["leading"]) row += 33 self.w.contents = TextBox((left, row, textboxWidth, 20), "Contents:", alignment="right") self.w.contentsEdit = TextEditor( (leftEditText, row, -10, 150), "\n".join(self.proofGroup["contents"])) self.w.contentsEdit.getNSTextView().setFont_(monoFont) row += 160 self.w.cancelButton = Button((leftEditText, row, 138, 20), "Cancel", callback=self.cancelCB) leftEditText += 147 self.w.okButton = Button((leftEditText, row, 138, 20), "OK", callback=self.okCB) self.w.setDefaultButton(self.w.okButton) self.w.bind("close", self._postCloseEvent) def _postCloseEvent(self, sender): postEvent("com.InspectorClosed") def _checkFloat(self, sender): """ Make sure users don't input non-floats by capturing value prior to new input, then using it if user tries to input an illegal character """ # pass # Store everything up to newly-typed character allButLast = sender.get()[:-1] try: float(sender.get()) # Get rid of whitespaces immediately sender.set(sender.get().strip()) except ValueError: sender.set(allButLast) def okCB(self, sender): """ Get everything from fields, save in self.editedProofGroup dict, post event, pass the edited group to observer, and close window """ self.editedProofGroup["name"] = self.w.groupNameEdit.get().strip() self.editedProofGroup["typeSize"] = self.w.typeSizeEdit.get() self.editedProofGroup["leading"] = self.w.leadingEdit.get() self.editedProofGroup["print"] = self.proofGroup[ "print"] # just pass this back for now self.editedProofGroup["contents"] = hf.makeCleanListFromStr( self.w.contentsEdit.get()) postEvent("com.ProofGroupEdited", editedProofGroup=self.editedProofGroup) self.w.close() def cancelCB(self, sender): self.w.close()
class Script( object ): def __init__(self): self.valuesPrefsKey = prefsKey + ".delta." + basename(Glyphs.font.filepath) # UI metrics spacing = 8 height = 22 # calculate window height minWinSize = (220, 120 + (len(Glyphs.font.masters) * (height + spacing))) # create UI window self.w = FloatingWindow( minWinSize, "Adjust sidebearings", minSize=minWinSize, maxSize=(500, 500), autosaveName=prefsKey + ".win") # layout UI controls y = 16 self.w.label = TextBox((16, y, -16, height), "Sidebearing delta adjustment:") y += height + spacing inputWidth = 64 for master in Glyphs.font.masters: setattr(self.w, "deltaLabel%s" % master.id, TextBox((16, y, -16-inputWidth, height), master.name)) setattr(self.w, "deltaInput%s" % master.id, EditText((-16-inputWidth, y, -16, height), "16")) # print("self.w.deltaInputs[master.id]", getattr(self.w, "deltaInput%s" % master.id)) y += height + spacing self.w.submitButton = Button( (16, -16-height, -16, height), "Adjust all sidebearings", callback=self.onSubmit) # finalize UI self.w.setDefaultButton(self.w.submitButton) self.loadPreferences() self.w.bind("close", self.savePreferences) # make sure window is large enough to show all masters x, y, w, h = self.w.getPosSize() if w < minWinSize[0] and h < minWinSize[1]: self.w.setPosSize((x, y, minWinSize[0], minWinSize[1]), animate=False) elif w < minWinSize[0]: self.w.setPosSize((x, y, minWinSize[0], h), animate=False) elif h < minWinSize[1]: self.w.setPosSize((x, y, w, minWinSize[1]), animate=False) self.w.open() self.w.makeKey() def getDeltaInputForMaster(self, masterId): return getattr(self.w, "deltaInput%s" % masterId) def loadPreferences(self): try: Glyphs.registerDefault(self.valuesPrefsKey, []) for t in Glyphs.defaults[self.valuesPrefsKey]: try: masterId, value = t self.getDeltaInputForMaster(masterId).set(value) except: pass except: print("failed to load preferences") def savePreferences(self, sender): try: values = [] for master in Glyphs.font.masters: values.append((master.id, self.getDeltaInputForMaster(master.id).get())) Glyphs.defaults[self.valuesPrefsKey] = values except: print("failed to save preferences") def onSubmit(self, sender): try: sender.enable(False) if performFontChanges(self.action1): self.w.close() except Exception(e): Glyphs.showMacroWindow() print("error: %s" % e) finally: sender.enable(True) def action1(self, font): print(font) deltas = {} # keyed on master.id for master in Glyphs.font.masters: deltas[master.id] = int(self.getDeltaInputForMaster(master.id).get()) syncLayers = set() # layers that need syncMetrics() to be called # [debug] alterntive loop over a few select glyphs, for debugging. # debugGlyphNames = set([ # ".null", # "A", "Adieresis", "Lambda", # "Bhook", # "C", "Chook", # "endash", # "space", # ]) # for g in [g for g in font.glyphs if g.name in debugGlyphNames]: for g in font.glyphs: # print(">> %s (%s)" % (g.name, g.productionName)) if g.name in excludedGlyphNames: # glyph is exlcuded print("ignoring excluded glyph %r" % g.name) continue g.beginUndo() try: for master in font.masters: layer = g.layers[master.id] delta = deltas[master.id] if delta == 0: # no adjustment continue if layer.width == 0: # ignore glyphs with zero-width layers print("ignoring zero-width glyph", g.name) break if layer.isAligned: # ignore glyphs which are auto-aligned from components print("ignoring auto-aligned glyph", g.name) break if len(layer.paths) == 0 and len(layer.components) == 0: # adjust width instead of LSB & RSB of empty glyphs if layer.widthMetricsKey is None: layer.width = max(0, layer.width + (delta * 2)) else: syncLayers.add(layer) continue # offset components by delta to counter-act effect of also applying delta # to the component glyphs. if layer.components is not None: for cn in layer.components: m = cn.transform # auto-aligned or offset x or offset y or contains shapes if not cn.automaticAlignment or m[4] != 0 or m[5] != 0 or len(layer.paths) > 0: cn.transform = ( m[0], # x scale factor m[1], # x skew factor m[2], # y skew factor m[3], # y scale factor m[4] - delta, # x position (+ since transform is inverse) m[5] # y position ) # Note: # layer metrics keys are expressions, e.g. "==H+10" # glyph metrics keys are other glyphs, e.g. U+0041 if g.leftMetricsKey is None and layer.leftMetricsKey is None: layer.LSB = layer.LSB + delta else: syncLayers.add(layer) if g.rightMetricsKey is None and layer.rightMetricsKey is None: layer.RSB = layer.RSB + delta else: syncLayers.add(layer) finally: g.endUndo() # end for g in font # sync layers that use metricsKey if len(syncLayers) > 0: print("Syncing LSB & RSB for %r layers..." % len(syncLayers)) for layer in syncLayers: layer.syncMetrics() print("Done")
class Adhesiontext(BaseWindowController): def __init__(self): flushAlign = 76 firstRowY = 12 rowOffsetY = 30 firstCheckY = 135 checkOffsetY = 27 rightMarginX = -12 self.windowWidth = 410 self.windowHeightWithoutOptions = 45 self.windowHeightWithOptions = 280 self.scriptIsRTL = False windowPos = getExtensionDefault("%s.%s" % (extensionKey, "windowPos")) if not windowPos: windowPos = (100, 100) self.optionsVisible = getExtensionDefault( "%s.%s" % (extensionKey, "optionsVisible")) if self.optionsVisible: optionsButtonSign = '-' windowHeight = self.windowHeightWithOptions else: self.optionsVisible = False # needs to be set because the first time the extension runs self.optionsVisible will be None optionsButtonSign = '+' windowHeight = self.windowHeightWithoutOptions self.chars = getExtensionDefault("%s.%s" % (extensionKey, "chars")) if not self.chars: self.chars = '' self.sliderValue = getExtensionDefault("%s.%s" % (extensionKey, "sliderValue")) if not self.sliderValue: self.sliderValue = 25 self.scriptsIndex = getExtensionDefault("%s.%s" % (extensionKey, "scriptsIndex")) if not self.scriptsIndex: self.scriptsIndex = 0 self.langsIndex = getExtensionDefault("%s.%s" % (extensionKey, "langsIndex")) if not self.langsIndex: self.langsIndex = 0 self.w = FloatingWindow( (windowPos[0], windowPos[1], self.windowWidth, windowHeight), "adhesiontext") # 1st row self.w.labelChars = TextBox((10, firstRowY, flushAlign, 20), "Characters:", alignment="right") self.w.chars = EditText((flushAlign + 15, firstRowY - 1, 199, 22), self.chars, callback=self.charsCallback) self.w.button = Button((300, firstRowY, 68, 20), "Get text", callback=self.buttonCallback) self.w.spinner = FixedSpinner((325, firstRowY, 20, 20), displayWhenStopped=False) self.w.optionsButton = SquareButton((378, firstRowY + 1, 18, 18), optionsButtonSign, sizeStyle="small", callback=self.optionsCallback) # set the initial state of the button according to the content of the chars EditText if len(self.w.chars.get()): self.w.button.enable(True) else: self.w.button.enable(False) # keep track of the content of chars EditText self.previousChars = self.w.chars.get() # 2nd row self.w.labelWords = TextBox( (10, firstRowY + rowOffsetY, flushAlign, 20), "Words:", alignment="right") self.w.wordCount = TextBox( (flushAlign + 12, firstRowY + rowOffsetY, 40, 20), alignment="left") self.w.slider = Slider( (flushAlign + 47, firstRowY + rowOffsetY + 1, 165, 20), value=self.sliderValue, minValue=5, maxValue=200, callback=self.sliderCallback) # set the initial wordCount value according to the position of the slider self.w.wordCount.set(int(self.w.slider.get())) # 3rd row self.w.labelScripts = TextBox( (10, firstRowY + rowOffsetY * 2, flushAlign, 20), "Script:", alignment="right") self.w.scriptsPopup = PopUpButton( (flushAlign + 15, firstRowY + rowOffsetY * 2, 150, 20), scriptsNameList, callback=self.scriptsCallback) self.w.scriptsPopup.set(self.scriptsIndex) # 4th row self.w.labelLangs = TextBox( (10, firstRowY + rowOffsetY * 3, flushAlign, 20), "Language:", alignment="right") self.w.langsPopup = PopUpButton( (flushAlign + 15, firstRowY + rowOffsetY * 3, 150, 20), []) # set the initial list of languages according to the script value self.w.langsPopup.setItems( langsNameDict[scriptsNameList[self.w.scriptsPopup.get()]]) self.w.langsPopup.set(self.langsIndex) self.punctCheck = getExtensionDefault("%s.%s" % (extensionKey, "punctCheck")) if not self.punctCheck: self.punctCheck = 0 self.figsCheck = getExtensionDefault("%s.%s" % (extensionKey, "figsCheck")) if not self.figsCheck: self.figsCheck = 0 self.figsPopup = getExtensionDefault("%s.%s" % (extensionKey, "figsPopup")) if not self.figsPopup: self.figsPopup = 0 self.trimCheck = getExtensionDefault("%s.%s" % (extensionKey, "trimCheck")) if not self.trimCheck: self.trimCheck = 0 self.caseCheck = getExtensionDefault("%s.%s" % (extensionKey, "caseCheck")) if not self.caseCheck: self.caseCheck = 0 self.casingCheck = getExtensionDefault("%s.%s" % (extensionKey, "casingCheck")) if not self.casingCheck: self.casingCheck = 0 self.casingPopup = getExtensionDefault("%s.%s" % (extensionKey, "casingPopup")) if not self.casingPopup: self.casingPopup = 0 # 1st checkbox self.w.punctCheck = CheckBox((flushAlign + 15, firstCheckY, 130, 20), "Add punctuation") self.w.punctCheck.set(self.punctCheck) # 2nd checkbox self.w.figsCheck = CheckBox( (flushAlign + 15, firstCheckY + checkOffsetY, 120, 20), "Insert numbers", callback=self.figsCallback) self.w.figsCheck.set(self.figsCheck) self.w.figsPopup = PopUpButton( (210, firstCheckY + checkOffsetY, 90, 20), figOptionsList) self.w.figsPopup.set(self.figsPopup) # enable or disable the figure options PopUp depending on the figures CheckBox if scriptsNameList[self.w.scriptsPopup.get()] in enableFigOptionList: self.w.figsPopup.show(True) if self.w.figsCheck.get(): self.w.figsPopup.enable(True) else: self.w.figsPopup.enable(False) else: self.w.figsPopup.show(False) # 3rd checkbox self.w.trimCheck = CheckBoxPlus( (flushAlign + 15, firstCheckY + checkOffsetY * 2, 120, 20), "Trim accents") self.w.trimCheck.set(self.trimCheck) if scriptsNameList[self.w.scriptsPopup.get()] in enableTrimCheckList: self.w.trimCheck.enable(True) else: self.w.trimCheck.enable(False) # 4th checkbox self.w.caseCheck = CheckBoxPlus( (flushAlign + 15, firstCheckY + checkOffsetY * 3, 120, 20), "Ignore casing") self.w.caseCheck.set(self.caseCheck) if scriptsNameList[self.w.scriptsPopup.get()] in enableCaseCheckList: self.w.caseCheck.enable(True) else: self.w.caseCheck.enable(False) # 5th checkbox self.w.casingCheck = CheckBoxPlus( (flushAlign + 15, firstCheckY + checkOffsetY * 4, 115, 20), "Change casing", callback=self.casingCallback) self.w.casingCheck.set(self.casingCheck) if scriptsNameList[self.w.scriptsPopup.get()] in enableCaseCheckList: self.w.casingCheck.enable(True) else: self.w.casingCheck.enable(False) self.w.casingPopup = PopUpButton( (210, firstCheckY + checkOffsetY * 4, 90, 20), casingNameList) self.w.casingPopup.set(self.casingPopup) # enable or disable the casing PopUp depending on the casing CheckBox if self.w.casingCheck.get() and self.w.casingCheck.isEnable(): self.w.casingPopup.enable(True) else: self.w.casingPopup.enable(False) self.nsTextField = self.w.chars.getNSTextField() self.w.setDefaultButton(self.w.button) self.w.bind("close", self.windowClose) self.w.open() self.w.makeKey() def windowClose(self, sender): self.saveExtensionDefaults() def saveExtensionDefaults(self): setExtensionDefault("%s.%s" % (extensionKey, "windowPos"), self.w.getPosSize()[0:2]) setExtensionDefault("%s.%s" % (extensionKey, "optionsVisible"), self.optionsVisible) setExtensionDefault("%s.%s" % (extensionKey, "chars"), self.w.chars.get()) setExtensionDefault("%s.%s" % (extensionKey, "sliderValue"), int(self.w.slider.get())) setExtensionDefault("%s.%s" % (extensionKey, "scriptsIndex"), int(self.w.scriptsPopup.get())) setExtensionDefault("%s.%s" % (extensionKey, "langsIndex"), int(self.w.langsPopup.get())) setExtensionDefault("%s.%s" % (extensionKey, "punctCheck"), self.w.punctCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "figsCheck"), self.w.figsCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "figsPopup"), self.w.figsPopup.get()) setExtensionDefault("%s.%s" % (extensionKey, "trimCheck"), self.w.trimCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "caseCheck"), self.w.caseCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "casingCheck"), self.w.casingCheck.get()) setExtensionDefault("%s.%s" % (extensionKey, "casingPopup"), self.w.casingPopup.get()) def buttonCallback(self, sender): sender.enable(False) self.w.spinner.start() self.getText() self.w.spinner.stop() sender.enable(True) def optionsCallback(self, sender): sign = sender.getTitle() if sign == "+": sender.setTitle("-") self.w.resize(self.windowWidth, self.windowHeightWithOptions, animate=True) self.optionsVisible = True else: sender.setTitle("+") self.w.resize(self.windowWidth, self.windowHeightWithoutOptions, animate=True) self.optionsVisible = False def charsCallback(self, sender): charsContent = sender.get() if len(charsContent): self.w.button.enable(True) nsTextView = self.nsTextField.currentEditor( ) # NOTE: the field editor is only available when NSTextField is in editing mode. # when only one glyph is selected and copied, the contents of the clipboard are the glyph's XML # instead of its unicode character or its name; therefore, post-process the pasted content. if xmlHeader in charsContent: caretIndex = charsContent.index(xmlHeader) codepointString = re_glyphUnicode.search(charsContent) glyphName = re_glyphName.search(charsContent) if codepointString: replacement = unichr(eval('0x' + codepointString.group(1))) elif glyphName: replacement = '/' + glyphName.group(1) else: replacement = '' # replace the glyph's XML by its unicode character or its name self.w.chars.set(re_glyph.sub(replacement, charsContent)) # restore the location of the caret location = caretIndex + len(replacement) nsTextView.setSelectedRange_((location, 0)) # update the variable charsContent = sender.get() caretIndex = nsTextView.selectedRanges()[0].rangeValue().location # Limit the number of characters numeralWasFound = self.stringHasNumeral(charsContent) if len(charsContent) > maxChars or numeralWasFound: NSBeep() if numeralWasFound: self.showMessage("Sorry, numerals are not allowed.", "") else: self.showMessage( "You've reached the maximum \rnumber of characters.", "The limit is %d." % maxChars) # restore the content of chars EditText to the previous string sender.set(self.previousChars) # restore the focus on the chars EditText and restore the location of the caret caretIndexAdjust = len(self.previousChars) - len(charsContent) self.w.getNSWindow().makeFirstResponder_(self.nsTextField) nsTextView.setSelectedRange_( (caretIndex + caretIndexAdjust, 0)) # update the stored string self.previousChars = sender.get() else: self.w.button.enable(False) def sliderCallback(self, sender): self.w.wordCount.set(int(sender.get())) def scriptsCallback(self, sender): self.w.langsPopup.setItems( langsNameDict[scriptsNameList[sender.get()]]) # toggle RTL/LTR if scriptsNameList[sender.get()] in rightToLeftList: self.scriptIsRTL = True self.nsTextField.setBaseWritingDirection_( NSWritingDirectionRightToLeft) self.nsTextField.setAlignment_(NSRightTextAlignment) else: self.scriptIsRTL = False self.nsTextField.setBaseWritingDirection_( NSWritingDirectionLeftToRight) self.nsTextField.setAlignment_(NSLeftTextAlignment) # restore the focus on the chars EditText self.w.getNSWindow().makeFirstResponder_(self.nsTextField) # toggle figsPopup if scriptsNameList[sender.get()] in enableFigOptionList: self.w.figsPopup.show(True) if self.w.figsCheck.get(): self.w.figsPopup.enable(True) else: self.w.figsPopup.enable(False) else: self.w.figsPopup.show(False) # toggle trimCheck if scriptsNameList[sender.get()] in enableTrimCheckList: self.w.trimCheck.enable(True) else: self.w.trimCheck.enable(False) # toggle caseCheck and casingCheck if scriptsNameList[sender.get()] in enableCaseCheckList: self.w.caseCheck.enable(True) self.w.casingCheck.enable(True) if self.w.casingCheck.get(): self.w.casingPopup.enable(True) else: self.w.caseCheck.enable(False) self.w.casingCheck.enable(False) self.w.casingPopup.enable(False) def figsCallback(self, sender): if sender.get(): self.w.figsPopup.enable(True) else: self.w.figsPopup.enable(False) def casingCallback(self, sender): if sender.get(): self.w.casingPopup.enable(True) else: self.w.casingPopup.enable(False) def stringHasNumeral(self, string): if re_numeral.search(string): return True return False def isConnected(self): try: urlopen(url, timeout=3) return True except URLError: pass return False def getText(self): if CurrentFont() is None: NSBeep() self.showMessage("Open a font first.", "") return if not self.isConnected(): NSBeep() self.showMessage("Required internet connection not found.", "") return values = { 'chars': self.w.chars.get().encode('utf-8'), 'script': scriptsTagDict[scriptsNameList[self.w.scriptsPopup.get()]], 'tb': langsTagDict[langsNameDict[scriptsNameList[ self.w.scriptsPopup.get()]][self.w.langsPopup.get()]] } if self.w.punctCheck.get(): values['punct'] = True if self.w.figsCheck.get(): values['figs'] = True if self.w.figsPopup.isVisible(): figsOptTagsList = ["dflt", "locl"] values['figsOpt'] = figsOptTagsList[self.w.figsPopup.get()] if self.w.trimCheck.get() and self.w.trimCheck.isEnable(): values['trim'] = True if self.w.caseCheck.get() and self.w.caseCheck.isEnable(): values['case'] = True if self.w.casingCheck.get() and self.w.casingCheck.isEnable(): values['casing'] = casingNameList[self.w.casingPopup.get()].lower() data = urlencode(values) data = data.encode('utf-8') print(data) request = Request(url, data) response = urlopen(request) text = response.read() textU = unicode(text, 'utf-8') if (msgStr in textU): textU = textU.replace(msgStr, "") NSBeep() self.showMessage(textU, "") return elif (wrnStr in textU): resultIndex = textU.find(rsltStr) secmsgIndex = textU.find(sndStr) frstmsgU = textU[:secmsgIndex].replace(wrnStr, "") scndmsgU = textU[secmsgIndex:resultIndex].replace(sndStr, "") textU = textU[resultIndex:].replace(rsltStr, "") NSBeep() self.showMessage(frstmsgU, scndmsgU) textList = textU.split() trimmedText = ' '.join(textList[:int(self.w.slider.get())]) if CurrentSpaceCenter() is None: OpenSpaceCenter(CurrentFont(), newWindow=False) sp = CurrentSpaceCenter() print(trimmedText) sp.setRaw(trimmedText) # Toggle RTL-LTR try: sp.setLeftToRight(not self.scriptIsRTL) sp.setInputWritingDirection( 'Right to Left' if self.scriptIsRTL else 'Left to Right') except AttributeError: pass return