class OCCToucheTool(): def __init__(self): NSUserDefaults.standardUserDefaults().registerDefaults_( {"ToucheWindowHeight": 340}) self.windowHeight = NSUserDefaults.standardUserDefaults( ).integerForKey_("ToucheWindowHeight") self.minWindowHeight = 340 if self.windowHeight < self.minWindowHeight: self.windowHeight = self.minWindowHeight self.closedWindowHeight = 100 self.w = Window((180, self.windowHeight), u'2ché!', minSize=(180, 340), maxSize=(250, 898)) self.w.bind("resize", self.windowResized) self.isResizing = False p = 10 w = 160 # options self.w.options = Group((0, 0, 180, 220)) buttons = { "checkSelBtn": { "text": "Check selected glyphs", "callback": self.checkSel, "y": p }, } for button, data in buttons.iteritems(): setattr( self.w.options, button, Button((p, data["y"], w - 22, 22), data["text"], callback=data["callback"], sizeStyle="small")) self.w.options.zeroCheck = CheckBox((p, 35, w, 20), "Ignore zero-width glyphs", value=True, sizeStyle="small") self.w.options.progress = ProgressSpinner((w - 8, 13, 16, 16), sizeStyle="small") # Modification(Nic): prev / next buttons self.w.options.prevButton = Button((w + 22, 35, 10, 10), "Prev Button", callback=self.prevPair_) self.w.options.prevButton.bind("uparrow", []) self.w.options.nextButton = Button((w + 22, 35, 10, 10), "Next Button", callback=self.nextPair_) self.w.options.nextButton.bind("downarrow", []) # /Modification(Nic) # Modification(Nic): prev / next buttons self.w.options.decrementKerningHigh = Button( (w + 22, 35, 10, 10), "Decrement Kerning High", callback=self.decrementKerningByHigh_) self.w.options.decrementKerningHigh.bind("leftarrow", ["command"]) self.w.options.decrementKerningLow = Button( (w + 22, 35, 10, 10), "Decrement Kerning Low", callback=self.decrementKerningByLow_) self.w.options.decrementKerningLow.bind("leftarrow", []) self.w.options.incrementKerningHigh = Button( (w + 22, 35, 10, 10), "Increment Kerning High", callback=self.incrementKerningByHigh_) self.w.options.incrementKerningHigh.bind("rightarrow", ["command"]) self.w.options.incrementKerningLow = Button( (w + 22, 35, 10, 10), "Increment Kerning Low", callback=self.incrementKerningByLow_) self.w.options.incrementKerningLow.bind("rightarrow", []) # /Modification(Nic) # results self.w.results = Group((0, 220, 180, -0)) self.w.results.show(False) textBoxes = {"stats": -34, "result": -18} for box, y in textBoxes.iteritems(): setattr(self.w.results, box, TextBox((p, y, w, 14), "", sizeStyle="small")) # list and preview self.w.outputList = List((0, 58, -0, -40), [{ "left glyph": "", "right glyph": "" }], columnDescriptions=[{ "title": "left glyph", "width": 90 }, { "title": "right glyph" }], showColumnTitles=False, allowsMultipleSelection=False, enableDelete=False, selectionCallback=self.showPair) self.w.outputList._setColumnAutoresizing() self._resizeWindow(False) self.w.open() # callbacks # Modification(Nic): Incrementing and Decrementing the Pair Index in the list. def prevPair_(self, sender=None): currentIndices = self.w.outputList.getSelection() prevIndices = map(lambda i: max(i - 1, 0), currentIndices) if (len(self.w.outputList) > 0): self.w.outputList.setSelection(prevIndices) def nextPair_(self, sender=None): currentIndices = self.w.outputList.getSelection() nextIndices = map(lambda i: min(i + 1, len(self.w.outputList) - 1), currentIndices) if (len(self.w.outputList) > 0): self.w.outputList.setSelection(nextIndices) # /Modification(Nic) # Modification(Nic): Incrementing and Decrementing the Pair Index in the list. def decrementKerningByLow_(self, sender=None): print('dec by low') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementLow'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], -increment) def decrementKerningByHigh_(self, sender=None): print('dec by high') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementHigh'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], -increment) def incrementKerningByLow_(self, sender=None): print('inc by low') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementLow'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], increment) def incrementKerningByHigh_(self, sender=None): print('inc by high') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementHigh'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], increment) # /Modification(Nic) # Modification(Nic): Routine to increment/decrement kerning properly def bumpKerningForPair(self, left, right, increment): leftGlyph, rightGlyph = Glyphs.font[left], Glyphs.font[right] k = Glyphs.font.kerningForPair(Glyphs.font.selectedFontMaster.id, leftGlyph.rightKerningKey, rightGlyph.leftKerningKey) if k > 10000: k = 0 # Glyphs uses MAXINT to signal no kerning. Glyphs.font.setKerningForPair(Glyphs.font.selectedFontMaster.id, leftGlyph.rightKerningKey, rightGlyph.leftKerningKey, k + increment) # Modification(Nic) def checkAll(self, sender=None): self.check(useSelection=False) def checkSel(self, sender=None): self.check(useSelection=True) def check(self, useSelection): self._resizeWindow(enlarge=False) self.checkFont(useSelection=useSelection, excludeZeroWidth=self.w.options.zeroCheck.get()) def showPair(self, sender=None): try: index = sender.getSelection()[0] glyphs = [self.f[gName] for gName in self.touchingPairs[index]] ActiveFont = self.f._font EditViewController = ActiveFont.currentTab if EditViewController is None: tabText = "/%s/%s" % (glyphs[0].name, glyphs[1].name) ActiveFont.newTab(tabText) else: textStorage = EditViewController.graphicView() if not hasattr(textStorage, "replaceCharactersInRange_withString_" ): # compatibility with API change in 2.5 textStorage = EditViewController.graphicView().textStorage( ) LeftChar = ActiveFont.characterForGlyph_(glyphs[0]._object) RightChar = ActiveFont.characterForGlyph_(glyphs[1]._object) if LeftChar != 0 and RightChar != 0: selection = textStorage.selectedRange() if selection.length < 2: selection.length = 2 if selection.location > 0: selection.location -= 1 NewString = "" if LeftChar < 0xffff and RightChar < 0xffff: NewString = u"%s%s" % (unichr(LeftChar), unichr(RightChar)) else: print "Upper plane codes are not supported yet" textStorage.replaceCharactersInRange_withString_( selection, NewString) selection.length = 0 selection.location += 1 textStorage.setSelectedRange_(selection) #self.w.preview.set(glyphs) except IndexError: pass # checking def _hasSufficientWidth(self, g): # to ignore combining accents and the like if self.excludeZeroWidth: # also skips 1-unit wide glyphs which many use instead of 0 if g.width < 2 or g._object.subCategory == "Nonspacing": return False return True def _trimGlyphList(self, glyphList): newGlyphList = [] for g in glyphList: if g.box is not None and self._hasSufficientWidth(g): newGlyphList.append(g) return newGlyphList def windowResized(self, window): posSize = self.w.getPosSize() Height = posSize[3] if Height > self.closedWindowHeight and self.isResizing is False: print "set new Height", Height NSUserDefaults.standardUserDefaults().setInteger_forKey_( Height, "ToucheWindowHeight") self.windowHeight = Height def _resizeWindow(self, enlarge=True): posSize = self.w.getPosSize() if enlarge: self.w.results.show(True) self.w.outputList.show(True) targetHeight = self.windowHeight if targetHeight < 340: targetHeight = 340 else: self.w.results.show(False) self.w.outputList.show(False) targetHeight = self.closedWindowHeight self.isResizing = True self.w.setPosSize((posSize[0], posSize[1], posSize[2], targetHeight)) self.isResizing = False # ok let's do this def checkFont(self, useSelection=False, excludeZeroWidth=True): f = CurrentFont() if f is not None: # initialize things self.w.options.progress.start() time0 = time.time() self.excludeZeroWidth = excludeZeroWidth self.f = f glyphNames = f.selection if useSelection else f.keys() glyphList = [f[x] for x in glyphNames] glyphList = self._trimGlyphList(glyphList) self.touchingPairs = OCCTouche(f).findTouchingPairs(glyphList) # display output self.w.results.stats.set("%d glyphs checked" % len(glyphList)) self.w.results.result.set("%d touching pairs found" % len(self.touchingPairs)) self.w.results.show(True) outputList = [{ "left glyph": g1, "right glyph": g2 } for (g1, g2) in self.touchingPairs] self.w.outputList.set(outputList) if len(self.touchingPairs) > 0: self.w.outputList.setSelection([0]) #self.w.preview.setFont(f) self.w.options.progress.stop() self._resizeWindow(enlarge=True) time1 = time.time() print u'2ché: finished checking %d glyphs in %.2f seconds' % ( len(glyphList), time1 - time0) else: Message(u'2ché: Can’t find a font to check')
class ToucheTool: def __init__(self): self.w = Window((180, 340), u"Touché!", minSize=(180, 340), maxSize=(1000, 898)) p = 10 w = 160 # options self.w.options = Group((0, 0, 180, 220)) buttons = { "checkSelBtn": {"text": "Check selected glyphs\nfor touching pairs", "callback": self.checkSel, "y": p}, "checkAllBtn": { "text": "Check entire font\n(can take several minutes!)", "callback": self.checkAll, "y": 60, }, } for button, data in buttons.iteritems(): setattr( self.w.options, button, SquareButton((p, data["y"], w, 40), data["text"], callback=data["callback"], sizeStyle="small"), ) self.w.options.zeroCheck = CheckBox((p, 108, w, 20), "Ignore zero-width glyphs", value=True, sizeStyle="small") self.w.options.progress = ProgressSpinner((82, 174, 16, 16), sizeStyle="small") # results self.w.results = Group((0, 220, 180, -0)) self.w.results.show(False) textBoxes = {"stats": 24, "result": 42} for box, y in textBoxes.iteritems(): setattr(self.w.results, box, TextBox((p, y, w, 14), "", sizeStyle="small")) moreButtons = { "spaceView": {"text": "View all in Space Center", "callback": self.showAllPairs, "y": 65}, "exportTxt": {"text": "Export as MM pair list", "callback": self.exportPairList, "y": 90}, } for button, data in moreButtons.iteritems(): setattr( self.w.results, button, SquareButton((p, data["y"], w, 20), data["text"], callback=data["callback"], sizeStyle="small"), ) # list and preview self.w.outputList = List( (180, 0, 188, -0), [{"left glyph": "", "right glyph": ""}], columnDescriptions=[{"title": "left glyph"}, {"title": "right glyph"}], showColumnTitles=False, allowsMultipleSelection=False, enableDelete=False, selectionCallback=self.showPair, ) self.w.preview = MultiLineView((368, 0, -0, -0), pointSize=256) self.w.open() # callbacks def checkAll(self, sender=None): self.check(useSelection=False) def checkSel(self, sender=None): self.check(useSelection=True) def check(self, useSelection): self.w.results.show(False) self._resizeWindow(enlarge=False) self.checkFont(useSelection=useSelection, excludeZeroWidth=self.w.options.zeroCheck.get()) def showPair(self, sender=None): try: index = sender.getSelection()[0] glyphs = [self.f[gName] for gName in self.touchingPairs[index]] self.w.preview.set(glyphs) except IndexError: pass def showAllPairs(self, sender=None): # open all resulting pairs in Space Center rawString = "" for g1, g2 in self.touchingPairs: rawString += "/%s/%s/space" % (g1, g2) s = OpenSpaceCenter(self.f) s.setRaw(rawString) def exportPairList(self, sender=None): # Save the list of found touching pairs in a text file which can be read by MetricsMachine as a pair list path = PutFile("Choose save location", "TouchingPairs.txt") if path is not None: reportString = "#KPL:P: TouchingPairs\n" for g1, g2 in self.touchingPairs: reportString += "%s %s\n" % (g1, g2) fi = open(path, "w+") fi.write(reportString) fi.close() # checking def _hasSufficientWidth(self, g): # to ignore combining accents and the like if self.excludeZeroWidth: # also skips 1-unit wide glyphs which many use instead of 0 if g.width < 2: return False return True def _trimGlyphList(self, glyphList): newGlyphList = [] for g in glyphList: if g.box is not None and self._hasSufficientWidth(g): newGlyphList.append(g) return newGlyphList def _resizeWindow(self, enlarge=True): posSize = self.w.getPosSize() targetWidth = 700 if enlarge else 180 self.w.setPosSize((posSize[0], posSize[1], targetWidth, posSize[3])) # ok let's do this def checkFont(self, useSelection=False, excludeZeroWidth=True): f = CurrentFont() if f is not None: # initialize things self.w.options.progress.start() time0 = time.time() self.excludeZeroWidth = excludeZeroWidth self.f = f glyphNames = f.selection if useSelection else f.keys() glyphList = [f[x] for x in glyphNames] glyphList = self._trimGlyphList(glyphList) self.touchingPairs = Touche(f).findTouchingPairs(glyphList) # display output self.w.results.stats.set("%d glyphs checked" % len(glyphList)) self.w.results.result.set("%d touching pairs found" % len(self.touchingPairs)) self.w.results.show(True) outputList = [{"left glyph": g1, "right glyph": g2} for (g1, g2) in self.touchingPairs] self.w.outputList.set(outputList) if len(self.touchingPairs) > 0: self.w.outputList.setSelection([0]) else: self.w.preview.set("") outputButtons = [self.w.results.spaceView, self.w.results.exportTxt] for b in outputButtons: b.enable(False) if len(self.touchingPairs) == 0 else b.enable(True) self.w.preview.setFont(f) self.w.options.progress.stop() self._resizeWindow(enlarge=True) time1 = time.time() print u"Touché: finished checking %d glyphs in %.2f seconds" % (len(glyphList), time1 - time0) else: Message(u"Touché: Can’t find a font to check")
class ToucheTool(): def __init__(self): self.w = Window((180, 340), u'Touché!', minSize=(180, 340), maxSize=(1000, 898)) p = 10 w = 160 # options self.w.options = Group((0, 0, 180, 220)) buttons = { "checkSelBtn": { "text": "Check selected glyphs\nfor touching pairs", "callback": self.checkSel, "y": p }, "checkAllBtn": { "text": "Check entire font\n(can take several minutes!)", "callback": self.checkAll, "y": 60 } } for button, data in buttons.items(): setattr( self.w.options, button, SquareButton((p, data["y"], w, 40), data["text"], callback=data["callback"], sizeStyle="small")) self.w.options.zeroCheck = CheckBox((p, 108, w, 20), "Ignore zero-width glyphs", value=True, sizeStyle="small") self.w.options.progress = ProgressSpinner((82, 174, 16, 16), sizeStyle="small") # results self.w.results = Group((0, 220, 180, -0)) self.w.results.show(False) textBoxes = {"stats": 24, "result": 42} for box, y in textBoxes.items(): setattr(self.w.results, box, TextBox((p, y, w, 14), "", sizeStyle="small")) moreButtons = { "spaceView": { "text": "View all in Space Center", "callback": self.showAllPairs, "y": 65 }, "exportTxt": { "text": "Export as MM pair list", "callback": self.exportPairList, "y": 90 } } for button, data in moreButtons.items(): setattr( self.w.results, button, SquareButton((p, data["y"], w, 20), data["text"], callback=data["callback"], sizeStyle="small")) # list and preview self.w.outputList = List((180, 0, 188, -0), [{ "left glyph": "", "right glyph": "" }], columnDescriptions=[{ "title": "left glyph" }, { "title": "right glyph" }], showColumnTitles=False, allowsMultipleSelection=False, enableDelete=False, selectionCallback=self.showPair) self.w.preview = MultiLineView((368, 0, -0, -0), pointSize=256) self.w.open() # callbacks def checkAll(self, sender=None): self.check(useSelection=False) def checkSel(self, sender=None): self.check(useSelection=True) def check(self, useSelection): self.w.results.show(False) self._resizeWindow(enlarge=False) self.checkFont(useSelection=useSelection, excludeZeroWidth=self.w.options.zeroCheck.get()) def showPair(self, sender=None): try: index = sender.getSelection()[0] glyphs = [self.f[gName] for gName in self.touchingPairs[index]] self.w.preview.set(glyphs) except IndexError: pass def showAllPairs(self, sender=None): # open all resulting pairs in Space Center rawString = "" for g1, g2 in self.touchingPairs: rawString += "/%s/%s/space" % (g1, g2) s = OpenSpaceCenter(self.f) s.setRaw(rawString) def exportPairList(self, sender=None): # Save the list of found touching pairs in a text file which can be read by MetricsMachine as a pair list path = PutFile(message="Choose save location", fileName="TouchingPairs.txt") if path is not None: reportString = "#KPL:P: TouchingPairs\n" for g1, g2 in self.touchingPairs: reportString += "%s %s\n" % (g1, g2) with open(path, 'w+') as fi: try: fi.write(reportString) except: #py2! fi.write(unicode(reportString)) # checking def _hasSufficientWidth(self, g): # to ignore combining accents and the like if self.excludeZeroWidth: # also skips 1-unit wide glyphs which many use instead of 0 if g.width < 2: return False return True def _trimGlyphList(self, glyphList): newGlyphList = [] for g in glyphList: bounds = g.bounds if version > '2.0' else g.box if bounds is not None and self._hasSufficientWidth(g): newGlyphList.append(g) return newGlyphList def _resizeWindow(self, enlarge=True): posSize = self.w.getPosSize() targetWidth = 700 if enlarge else 180 self.w.setPosSize((posSize[0], posSize[1], targetWidth, posSize[3])) # ok let's do this def checkFont(self, useSelection=False, excludeZeroWidth=True): f = CurrentFont() if f is not None: # initialize things self.w.options.progress.start() time0 = time.time() self.excludeZeroWidth = excludeZeroWidth self.f = f glyphNames = f.selection if useSelection else f.keys() glyphList = [f[x] for x in glyphNames] glyphList = self._trimGlyphList(glyphList) self.touchingPairs = Touche(f).findTouchingPairs(glyphList) # display output self.w.results.stats.set("%d glyphs checked" % len(glyphList)) self.w.results.result.set("%d touching pairs found" % len(self.touchingPairs)) self.w.results.show(True) outputList = [{ "left glyph": g1, "right glyph": g2 } for (g1, g2) in self.touchingPairs] self.w.outputList.set(outputList) if len(self.touchingPairs) > 0: self.w.outputList.setSelection([0]) else: self.w.preview.set("") outputButtons = [ self.w.results.spaceView, self.w.results.exportTxt ] for b in outputButtons: b.enable(False) if len( self.touchingPairs) == 0 else b.enable(True) self.w.preview.setFont(f) self.w.preview.setApplyKerning(True) self.w.options.progress.stop() self._resizeWindow(enlarge=True) time1 = time.time() print(u'Touché: finished checking %d glyphs in %.2f seconds' % (len(glyphList), time1 - time0)) else: Message(u'Touché: Can’t find a font to check')