コード例 #1
0
ファイル: copy_kerning.py プロジェクト: rbmntjs/copyKerning
class CopyKerning:

    def __init__(self):
        if AllFonts() is None:
            from vanilla.dialogs import message
            message("No fonts open.", "Open or create a font to copy data to and fro.")
            return

        self.sourceFontList = AllFonts()
        self.destinationFontList = AllFonts()
        self.source_font = self.sourceFontList[0]
        self.destination_fonts = None
        self.groups = None
        self.kerning = None

        ## create a window
        self.w = Window((400, 500), "Copy Groups and Kerning", minSize=(500, 600))
        self.w.sourceTitle = TextBox((15, 20, 200, 20), "Source Font:")
        self.w.sourceFont = PopUpButton((15, 42, 340, 20), [f.info.familyName + ' ' + f.info.styleName for f in self.sourceFontList], callback=self.sourceCallback)
        self.w.desTitle = TextBox((15, 76, 200, 20), "Destination Fonts:")
        self.w.destinationFonts = FontList((15, 96, -15, -115), self.destinationFontList, selectionCallback=self.desCallback)
        self.w.copyButton = Button((-215, -40, 200, 20), 'Copy Groups & Kerning', callback=self.copyCallback)
        self.w.line = HorizontalLine((10, -60, -10, 1))
        self._updateDest()
        ## open the window
        self.w.open()

    def _updateDest(self):
        des = list(self.sourceFontList)
        des.remove(self.source_font)
        self.w.destinationFonts.set(des)

    def copyKerning(self, groups, kerning, source_font, destination_fonts):
        kerning = source_font.kerning.asDict()
        groups = source_font.groups
        for font in destination_fonts:
            font.groups.update(groups)
            font.kerning.update(kerning)

    def sourceCallback(self, sender):
        self.source_font = self.sourceFontList[sender.get()]
        self._updateDest()

    def desCallback(self, sender):
        self.destination_fonts = [sender.get()[x] for x in sender.getSelection()]

    def copyCallback(self, sender):
        self.sheet = Sheet((300, 50), self.w)
        self.sheet.bar = ProgressBar((10, 20, -10, 10), isIndeterminate=True, sizeStyle="small")
        self.sheet.open()
        self.sheet.bar.start()
        self.copyKerning(self.groups, self.kerning, self.source_font, self.destination_fonts)
        self.sheet.bar.stop()
        self.sheet.close()
        del self.sheet
        self.w.close()
コード例 #2
0
class convertMasterToBraceLayers(object):
    def __init__(self):
        self.master_names = [m.name for m in Glyphs.font.masters][1:]

        item_height = 24.0
        w_width = 350.0
        w_height = item_height * 5
        margin = 10
        next_y = margin
        col_1_width = w_width - (margin * 2)
        item_height = 24

        self.w = Window((w_width, w_height), "Convert Master to Brace Layers")

        next_y = margin
        self.w.master_names_label = TextBox((margin, next_y + 2, col_1_width, item_height), "Master to Convert (Cannot be the first Master)", sizeStyle='regular')
        next_y += item_height
        self.w.master_names = PopUpButton((margin, next_y, col_1_width, item_height), self.master_names)
        next_y += item_height + margin
        selected_master_index = Glyphs.font.masters.index(Glyphs.font.selectedFontMaster)
        self.w.master_names.set(selected_master_index - 1)

        self.w.gobutton = Button((margin + (col_1_width / 4), next_y, col_1_width / 2, item_height), 'Add Brace Layers', callback=self.makeitso)

        self.w.setDefaultButton(self.w.gobutton)

        self.w.open()

    def makeitso(self, sender):
        self.w.close()

        Glyphs.font.disableUpdateInterface()

        selected_master_index = self.w.master_names.get() + 1
        selected_master = Glyphs.font.masters[selected_master_index]
        layer_name = '{{{}}}'.format(', '.join([bytes(x) for x in selected_master.axes]))
        master_to_attach_to = Glyphs.font.masters[selected_master_index - 1]

        for g in Glyphs.font.glyphs:
            for l in g.layers:
                if l.associatedMasterId == selected_master.id:
                    if l.isSpecialLayer:
                        l.associatedMasterId = master_to_attach_to.id
                        continue

                    newL = copy.copy(l)
                    newL.associatedMasterId = master_to_attach_to.id
                    newL.name = layer_name
                    g.layers.append(newL)

        del(Glyphs.font.masters[selected_master_index])

        Glyphs.font.enableUpdateInterface()
        Glyphs.showMacroWindow()
class GlyphnameDialog( object):

	def __init__( self ):
		x = 10
		y = 10
		height = 20
		button_width = 30
		glyphname_width = 180
		gap = 6
		self.w = Window( ( x + button_width + gap + glyphname_width + gap + button_width + x, y + height + y ), "insert glyph" )
		self.w.center()
		self.w.glyphname = EditText( ( x, y, glyphname_width, height ), '')
		x += glyphname_width + gap
		self.w.alignleft = Button( ( x, y, button_width, height ), LEFT, callback = self.buttonCallback )
		x += button_width + gap
		self.w.alignright = Button( ( x, y, button_width, height ), RIGHT, callback = self.buttonCallback )
		self.w.setDefaultButton( self.w.alignleft )
		self.w.alignright.bind( "\x1b", [] )
		self.w.open()

	def buttonCallback( self, sender ):
		alignment = sender.getTitle()
		glyphname = self.w.glyphname.get()
		if not glyphname:
			self.w.close()
			return
		if len( glyphname ) == 1:
			uni = ord(glyphname)
			g = font.glyphForUnicode_("%.4X" % uni)
			if g:
				glyphname = g.name
		other_glyph = font.glyphs[ glyphname ]
		if not other_glyph:
			for glyph in font.glyphs:
				if glyph.name.startswith( glyphname ):
					other_glyph = glyph
					print 'Using', glyph.name
					break
			else:
				print 'No matching glyph found.'
				self.w.close()
				return
		
		selected_glyphs = set( [ layer.parent for layer in font.selectedLayers ] )
		
		for glyph in selected_glyphs:
			glyph.beginUndo()
			for layer in glyph.layers:
				# find other layer
				for other_layer in other_glyph.layers:
					if other_layer.name == layer.name:
						insert_paths( layer, other_layer, alignment )
						break
				else:
					if active_layerId == layer.layerId:
						insert_paths( layer, other_glyph.layers[layer.associatedMasterId], alignment )
			glyph.endUndo()
		self.w.close()
コード例 #4
0
class GlyphnameDialog( object):

	def __init__( self ):
		hori_margin = 10
		verti_margin = hori_margin
		button_width = 30
		glyphname_width = 180
		line_height = 20
		gap = 9
		dialog_height = line_height + gap + line_height + gap + line_height
		dialog_width = button_width + gap + glyphname_width + gap + button_width
		self.w = Window( ( hori_margin + dialog_width + hori_margin, verti_margin + dialog_height + verti_margin ), "insert glyph" )
		self.w.center()
		x = hori_margin
		y = verti_margin
		# glyph name
		self.w.glyphname = EditText( ( x, y, glyphname_width, line_height ), '')
		self.w.glyphname.getNSTextField().setToolTip_( u'Enter the name of the glyph to be inserted. It is sufficient to enter the beginning of the glyph name, e.g. “deg” for “degree”.' )
		# buttons
		x += glyphname_width + gap
		self.w.alignleft = Button( ( x, y, button_width, line_height ), LEFT, callback = self.buttonCallback )
		self.w.alignleft.getNSButton().setToolTip_( 'Insert the other glyph left-aligned, i.e. at its original same position. Keyboard shortcut: Enter' )
		x += button_width + gap
		self.w.alignright = Button( ( x, y, button_width, line_height ), RIGHT, callback = self.buttonCallback )
		self.w.alignright.getNSButton().setToolTip_( 'Insert the other glyph right-aligned with respect to the advance widths. Keyboard shortcut: Esc' )
		self.w.setDefaultButton( self.w.alignleft )
		self.w.alignright.bind( "\x1b", [] )
		# insert as component
		as_component_is_checked = True
		if Glyphs.defaults["com.FMX.InsertGlyphToBackground.AsCompoment"]  is not None:
			as_component_is_checked = Glyphs.defaults["com.FMX.InsertGlyphToBackground.AsCompoment"]
		y += line_height + gap
		x = hori_margin
		self.w.as_component = CheckBox( ( x, y, dialog_width, line_height ), 'Insert as component', callback=None, value=as_component_is_checked )
		self.w.as_component.getNSButton().setToolTip_( 'If checked, the other glyph is inserted to the background as a component. Otherwise, it is inserted as paths (even if the other glyph is made of components).' )
		# clear current contents
		y += line_height + gap
		clear_contents_is_checked = True
		if Glyphs.defaults["com.FMX.InsertGlyphToBackground.ClearContents"]  is not None:
			clear_contents_is_checked = Glyphs.defaults["com.FMX.InsertGlyphToBackground.ClearContents"]
		self.w.clear_contents = CheckBox( ( x, y, dialog_width, line_height ), 'Clear current contents', callback=None, value=clear_contents_is_checked )
		self.w.clear_contents.getNSButton().setToolTip_( 'Check this to clear the background before inserting the other glyph. Uncheck to keep the current contents of the background.' )
		self.w.open()

	def buttonCallback( self, sender ):
		alignment = sender.getTitle()
		glyphname = self.w.glyphname.get()
		as_component_is_checked = self.w.as_component.get()
		clear_contents_is_checked = self.w.clear_contents.get()
		if not glyphname:
			self.w.close()
			return
		if len( glyphname ) == 1:
			uni = ord(glyphname)
			g = font.glyphForUnicode_("%.4X" % uni)
			if g:
				glyphname = g.name
		other_glyph = font.glyphs[ glyphname ]
		if not other_glyph:
			for glyph in font.glyphs:
				if glyph.name.startswith( glyphname ):
					other_glyph = glyph
					break
			else:
				self.w.close()
				return
		
		selected_glyphs = set( [ layer.parent for layer in font.selectedLayers ] )
		
		for glyph in selected_glyphs:
			glyph.beginUndo()
			for layer in glyph.layers:
				# find other layer
				for other_layer in other_glyph.layers:
					if other_layer.name == layer.name:
						insert_paths( layer, other_layer, alignment, as_component_is_checked, clear_contents_is_checked )
						break
				else:
					if layer.isBraceLayer:
						# the corresponding brace layer was not found in other_glyph.
						# let’s interpolate it on-the-fly:
						other_glyph_copy = other_glyph.copy()
						other_glyph_copy.parent = font
						# ^ Glyphs needs the font’s master coordinates for the re-interpolation
						interpolatedLayer = GSLayer()
						interpolatedLayer.name = layer.name
						# ^ necessary for the re-interpolation
						other_glyph_copy.layers.append( interpolatedLayer )
						interpolatedLayer.reinterpolate()
						insert_paths( layer, interpolatedLayer, alignment, as_component = False, clear_contents = clear_contents_is_checked )
					elif active_layerId == layer.layerId:
						insert_paths( layer, other_glyph.layers[layer.associatedMasterId], alignment, as_component_is_checked, clear_contents_is_checked )
			glyph.endUndo()
		Glyphs.defaults["com.FMX.InsertGlyphToBackground.AsCompoment"] = as_component_is_checked
		Glyphs.defaults["com.FMX.InsertGlyphToBackground.ClearContents"] = clear_contents_is_checked
		self.w.close()
コード例 #5
0
class copyKerning(object):
    # TODO: Add class to different class name copying.
    # TODO: Allow left or right or both selection.
    # TODO: Allow open font selection.
    # TODO: Allow from/to layer selection.

    def __init__(self):
        item_height = 24.0
        margin = 10
        next_y = margin
        w_width = 400.0
        w_height = item_height * 7 + margin
        col_1_width = w_width - (margin * 2)

        self.this_font = Glyphs.font
        try:
            self.other_font = [f for f in Glyphs.fonts
                               if f != self.this_font][0]
        except IndexError:
            Glyphs.showNotification('Copy kerning for Class from Other Font:',
                                    'There is only 1 file open!')
            raise

        self.other_fonts_classes = self.get_other_fonts_classes()
        self.w = Window((w_width, w_height),
                        "Copy kerning for Class from Other Font")

        self.w.text_1 = TextBox(
            (margin, next_y, w_width, item_height),
            "Copy the kerning for this class to this font:",
            sizeStyle='small')
        next_y += item_height
        self.w.class_to_copy = PopUpButton(
            (margin, next_y, w_width - (margin * 2), item_height),
            self.other_fonts_classes,
            sizeStyle='regular')
        next_y += item_height + item_height

        self.w.copy_for_all = RadioGroup(
            (margin, next_y, w_width, item_height * 2), [
                ' Copy only for the current Master',
                ' Copy for All masters',
            ])
        self.w.copy_for_all.set(0)
        next_y += (item_height * 2) + margin

        self.w.gobutton = Button(
            (margin + (col_1_width / 4), next_y, col_1_width / 2, item_height),
            'Copy',
            callback=self.makeitso)

        self.w.setDefaultButton(self.w.gobutton)
        self.w.center()
        self.w.open()

    def get_other_fonts_classes(self):
        all_kern_groups = set()
        for g in self.other_font.glyphs:
            if g.leftKerningGroup is not None:
                all_kern_groups.add(g.leftKerningGroup)

            if g.rightKerningGroup is not None:
                all_kern_groups.add(g.rightKerningGroup)

        return sorted(all_kern_groups)

    def get_other_master(self, this_master):
        try:
            other_master = [
                m for m in self.other_font.masters
                if m.name == this_master.name
            ][0]
            return other_master
        except IndexError:
            pass

        return self.other_font.selectedFontMaster

    @staticmethod
    def glyph_for_id(font, some_name):
        try:
            return font.glyphForId_(some_name).name
        except AttributeError:
            return some_name

    def get_this_glyphname(self, some_name):
        try:
            return Glyphs.font.glyphForId_(some_name).name
        except AttributeError:
            return some_name

    def copy_left_kerning(self, this_master, class_name):
        mmk_class_name = '@MMK_L_{}'.format(class_name)
        for right, val in self.other_font.kerning[self.get_other_master(
                this_master).id][mmk_class_name].items():
            if right.startswith('@MMK'):
                right_name = right
            else:
                right_name = self.other_font.glyphForId_(right).name

            self.this_font.setKerningForPair(this_master.id, mmk_class_name,
                                             right_name, val)

    def copy_right_kerning(self, this_master, class_name):
        # TODO: This. Like, it's not done.
        print(
            'No left-side kerning was copied because the function hasn\'t been written yet.'
        )
        pass

    def makeitso(self, sender):
        self.w.close()
        class_name = self.w.class_to_copy.getItem()
        print(bool(self.w.copy_for_all.get()))
        chosen_masters = self.this_font.masters if self.w.copy_for_all.get(
        ) else [self.this_font.selectedFontMaster]
        for m in chosen_masters:
            self.copy_left_kerning(m, class_name)
            self.copy_right_kerning(m, class_name)
コード例 #6
0
ファイル: dialogs.py プロジェクト: michielkauwatjoe/Meta
class Dialogs(object):
    u"""
    New dialog creation, for dialog closing see Callbacks class.
    """

    # Start.

    def openStartWindow(self):
        u"""
        Offers a 'New' and 'Open...' button.
        """
        self.startDialog = Window(self.dialogSize, "Welcome", minSize=self.dialogSize, maxSize=self.dialogSize)
        self.startDialog.newText = TextBox((20, 60, 60, 30), "New")
        self.startDialog.newButton = Button((20, 100, 80, 20), "Create...", callback=self.new_)
        self.startDialog.openText = TextBox((160, 60, 60, 30), "Existing")
        self.startDialog.openButton = Button((160, 100, 60, 20), "Open...", callback=self.open_)
        self.startDialog.open()

    def closeStartDialog(self):
        u"""
        Closes and clears start dialog.
        """
        if not self.startDialog is None:
            self.startDialog.close()
            self.startDialog = None

    # Document.

    def openOpenDocumentDialog(self):
        u"""
        Open document dialog.
        """
        self.closeStartDialog()

        panel = NSOpenPanel.openPanel()
        panel.setCanChooseDirectories_(False)
        panel.setCanChooseFiles_(True)
        panel.setAllowsMultipleSelection_(False)
        panel.setAllowedFileTypes_(['wf'])

        if panel.runModal() == NSOKButton:
            added, document = self.model.openDocument(panel.filenames()[0])
            self.initDocument(added, document)

    def openNewDialog(self):
        u"""
        Opens the document canvas if it doesn't exist yet.
        """
        self.closeStartDialog()
        self.setDefaultDocumentValues()

        size = (400, 300)
        self.newDialog = w = Window(size, "New Document", minSize=size, maxSize=size)

        w.nameText = TextBox((20, 20, 180, 20), "Name")
        w.nameBox = EditText((200, 20, 180, 20), callback=self.newNameCallback)
        w.nameBox.set(self.documentValues['documentName'])

        w.sizeText = TextBox((20, 40, 180, 20), "Size")
        values = PaperSizes.getAllPaperSizes()
        w.sizeComboBox = ComboBox((200, 40, 180, 20), values, callback=self.newSizeCallback)
        w.sizeComboBox.set(self.defaultPaperSize)

        w.whText = TextBox((20, 60, 220, 20), u"w×h in ㎜")

        width, height = PaperSizes.getSize(self.defaultPaperSize, 'mm')

        w.width = EditText((200, 60, 80, 20), callback=self.newWidthCallback)
        w.width.set(width)

        w.x = TextBox((285, 60, 10, 20), u"×")
        w.dimensionBoxHeight = EditText((300, 60, 80, 20), callback=self.newHeightCallback)
        w.dimensionBoxHeight.set(height)

        w.okayButton = Button((240, 260, 60, 20), "Okay", callback=self.newOkayCallback)
        #w.okayButton.getNSButton().setEnabled_(False)
        w.cancelButton = Button((320, 260, 60, 20), "Cancel",
                callback=self.newCancelCallback)
        w.open()

    def closeOpenDialog(self):
        if not self.openDialog is None:
            self.openDialog.close()
            self.openDialog = None

    # Save dialog.

    def openSaveDialog(self):
        if not self.saveDialog is None:
            return

        self.saveDialog = Window(self.saveDialogSize, "Save", minSize=self.saveDialogSize,
                                    maxSize=self.saveDialogSize)
        self.saveDialog.saveText = TextBox((60, 20, 280, 60),
                "Save changes to the document %s?" % self.currentDocument.name)

        self.saveDialog.dontButton = Button((60, 70, 100, 20), "Don't Save",
                callback=self.saveDontCallback)

        self.saveDialog.cancelButton = Button((260, 70, 60, 20), "Cancel",
                callback=self.saveCloseCallback)

        self.saveDialog.doButton = Button((320, 70, 60, 20), "Save",
            callback=self.saveDoCallback)
        self.saveDialog.open()

    def closeSaveDialog(self):
        self.saveDocumentDialog.close()
        self.saveDocumentDialog = None

    # Close.

    def windowShouldCloseCallback(self, sender):
        window = self.getCurrentWindow()

        if sender == window:
            self.closeDocument_(sender)
コード例 #7
0
class renameKerning(object):
    def __init__(self):
        item_height = 24.0
        w_width = 300.0
        w_height = item_height * 8
        margin = 10
        next_y = margin
        col_1_width = w_width - (margin * 2)
        item_height = 24

        self.messages = []
        self.interpolated_fonts = dict()
        self.current_glyph = None

        self.all_kern_groups = self.get_all_kern_groups()
        self.w = Window((w_width, w_height), "Rename Kern Groups")

        self.w.text_1 = TextBox((margin, next_y, w_width, item_height),
                                "Rename:",
                                sizeStyle='small')
        next_y += item_height
        self.w.nameFind = PopUpButton(
            (margin, next_y, w_width - (margin * 2), item_height),
            self.all_kern_groups,
            sizeStyle='regular')
        next_y += item_height + item_height

        self.w.text_2 = TextBox((margin, next_y, w_width, item_height),
                                "To:",
                                sizeStyle='small')
        next_y += item_height
        self.w.nameReplace = EditText(
            (margin, next_y, w_width - (margin * 2), item_height),
            "",
            sizeStyle='regular')
        next_y += item_height + margin

        self.w.gobutton = Button(
            (margin + (col_1_width / 4), next_y, col_1_width / 2, item_height),
            'Rename',
            callback=self.makeitso)

        self.w.setDefaultButton(self.w.gobutton)
        self.w.center()
        self.w.open()

    def sbuttonCallback(self, sender):
        self.s.close()

    def get_all_kern_groups(self):
        all_kern_groups = set()
        for g in Glyphs.font.glyphs:
            if g.leftKerningGroup is not None:
                all_kern_groups.add('{} - LEFT'.format(g.leftKerningGroup))
                if '{} - RIGHT'.format(g.leftKerningGroup) in all_kern_groups:
                    all_kern_groups.add('{} - BOTH'.format(g.leftKerningGroup))

            if g.rightKerningGroup is not None:
                all_kern_groups.add('{} - RIGHT'.format(g.rightKerningGroup))
                if '{} - LEFT'.format(g.rightKerningGroup) in all_kern_groups:
                    all_kern_groups.add('{} - BOTH'.format(
                        g.rightKerningGroup))

        return sorted(all_kern_groups)

    def makeitso(self, sender):
        self.w.close()
        nameReplace = self.w.nameReplace.get()
        nameFind = self.w.nameFind.getItem()
        group_name, _, side = nameFind.partition(' - ')

        for g in Glyphs.font.glyphs:
            if side in ['LEFT', 'BOTH']:
                if g.leftKerningGroup == group_name:
                    g.leftKerningGroup = nameReplace

            if side in ['RIGHT', 'BOTH']:
                if g.rightKerningGroup == group_name:
                    g.rightKerningGroup = nameReplace

        if '{} - {}'.format(nameReplace, side) in self.all_kern_groups:
            Glyphs.showNotification(
                'Kern Group Renaming Success!',
                'Note: the kern group named "{}" already exists so it will keep its existing kerning values.'
                .format(nameReplace))
            return

        for m_id in Glyphs.font.kerning:
            for left_group in Glyphs.font.kerning[m_id]:
                if side in ['RIGHT', 'BOTH']:
                    group_prefix = self.is_group(left_group, group_name)
                    if group_prefix:
                        for right_group, val in Glyphs.font.kerning[m_id][
                                left_group].items():
                            Glyphs.font.setKerningForPair(
                                m_id, group_prefix + nameReplace,
                                self.glyph_for_id(right_group), val)
                            Glyphs.font.removeKerningForPair(
                                m_id, left_group,
                                self.glyph_for_id(right_group))
                        left_group = group_prefix + nameReplace

                if side in ['LEFT', 'BOTH']:
                    for right_group, val in Glyphs.font.kerning[m_id][
                            left_group].items():
                        group_prefix = self.is_group(right_group, group_name)
                        if group_prefix:
                            # print(m_id, left_group, right_group, self.glyph_for_id(left_group), group_prefix + nameReplace, val)
                            # with open('/Users/benjones/Desktop/test.txt', 'a') as f:
                            #     f.write('{}\n'.format([m_id, left_group, right_group, self.glyph_for_id(left_group), group_prefix + nameReplace, val]))
                            Glyphs.font.setKerningForPair(
                                m_id, self.glyph_for_id(left_group),
                                group_prefix + nameReplace, val)
                            Glyphs.font.removeKerningForPair(
                                m_id, self.glyph_for_id(left_group),
                                right_group)

    @staticmethod
    def is_group(group, group_name):
        found_group = re.match('^(@MMK_._){}$'.format(group_name), group)
        if found_group is None:
            return False

        return found_group.group(1)

    @staticmethod
    def glyph_for_id(some_name):
        try:
            return Glyphs.font.glyphForId_(some_name).name
        except AttributeError:
            return some_name
コード例 #8
0
class GlyphnameDialog(object):
    def __init__(self):
        x = 10
        y = 10
        height = 20
        button_width = 30
        glyphname_width = 180
        gap = 6
        self.w = Window(
            (x + button_width + gap + glyphname_width + gap + button_width + x,
             y + height + y), "insert glyph")
        self.w.center()
        self.w.glyphname = EditText((x, y, glyphname_width, height), '')
        x += glyphname_width + gap
        self.w.alignleft = Button((x, y, button_width, height),
                                  LEFT,
                                  callback=self.buttonCallback)
        x += button_width + gap
        self.w.alignright = Button((x, y, button_width, height),
                                   RIGHT,
                                   callback=self.buttonCallback)
        self.w.setDefaultButton(self.w.alignleft)
        self.w.alignright.bind("\x1b", [])
        self.w.open()

    def buttonCallback(self, sender):
        title = sender.getTitle()
        glyphname = self.w.glyphname.get()
        if not glyphname:
            self.w.close()
            return
        if len(glyphname) == 1:
            uni = ord(glyphname)
            g = font.glyphForUnicode_("%.4X" % uni)
            if g:
                glyphname = g.name
        other_glyph = font.glyphs[glyphname]
        if not other_glyph:
            for glyph in font.glyphs:
                if glyph.name.startswith(glyphname):
                    other_glyph = glyph
                    break
            else:
                self.w.close()
                return

        for layer in font.selectedLayers:
            glyph = layer.parent
            glyph.beginUndo()
            # deselect all
            for path in layer.paths:
                for node in path.nodes:
                    layer.removeObjectFromSelection_(node)
            # find other layer
            for other_layer in other_glyph.layers:
                if other_layer.name == layer.name:
                    # insert paths
                    for path in other_layer.copyDecomposedLayer().paths:
                        if title == RIGHT:
                            shift = layer.width - other_layer.width
                            for node in path.nodes:
                                node.x = node.x + shift
                        layer.paths.append(path)
                        # select path
                        layer.paths[-1].selected = True
                    break
            glyph.endUndo()
        self.w.close()
コード例 #9
0
class makeDisplay(object):
    def __init__(self):
        self.verboten = {
            'right': ['napostrophe', 'Omegadasiavaria'],
            'left': ['ldot', 'Ldot', 'ldot.sc', 'sigmafinal'],
            'both': ['*.tf', '*.tosf', '.notdef', 'NULL', 'CR']
        }
        self.category = None
        self.messages = []
        self.interpolated_fonts = dict()
        self.use_real = True
        self.use_selection = False
        self.ignore_red = False
        self.current_glyph = None
        self.leftside_kerning_groups = None
        self.rightside_kerning_groups = None
        self.all_kern_categories = self.get_all_kern_categories()
        self.categories_leftside = self.get_categorised_glyphs('left')
        self.categories_rightside = self.get_categorised_glyphs('right')

        item_height = 24.0
        w_width = 300.0
        w_height = item_height * (7 + len(self.all_kern_categories))
        margin = 10
        next_y = margin
        col_1_width = w_width - (margin * 2)
        item_height = 24

        radio_height = item_height * len(self.all_kern_categories)

        self.w = Window((w_width, w_height), "Make Kerning Strings")

        self.w.text_1 = TextBox((margin, next_y, w_width, item_height), "Kern with:", sizeStyle='regular')
        next_y += item_height
        self.w.radioCategories = RadioGroup((margin, next_y, col_1_width, radio_height), self.all_kern_categories, sizeStyle='regular')
        self.w.radioCategories.set(0)
        next_y += radio_height + margin

        self.w.use_real = CheckBox((margin, next_y, col_1_width, item_height), "Use real words", value=True, sizeStyle='regular')
        next_y += item_height

        self.w.use_selected = CheckBox((margin, next_y, col_1_width, item_height), "Use the selected glyphs verbatum", value=False, sizeStyle='regular')
        next_y += item_height

        self.w.ignore_red = CheckBox((margin, next_y, col_1_width, item_height), "Ignore red marked glyphs", value=False, sizeStyle='regular')
        next_y += item_height + margin

        self.w.gobutton = Button((margin + (col_1_width / 4), next_y, col_1_width / 2, item_height), 'Make Strings', callback=self.makeitso)

        self.w.setDefaultButton(self.w.gobutton)
        self.w.center()
        self.w.open()
        # self.makeitso(None)

    def sbuttonCallback(self, sender):
        self.s.close()

    @staticmethod
    def has_smallcaps():
        for g in Glyphs.font.glyphs:
            if g.subCategory == 'Smallcaps':
                return True

        return False

    def get_all_kern_categories(self):
        kcats = [
            'Uppercase',
            'Lowercase',
        ]
        if self.has_smallcaps:
            kcats.append('Smallcaps')
        kcats += [
            'Quotes',
            'Number',
            'Punctuation',
            'Other',
        ]
        return kcats

    def get_canonincal_kerning_glyph(self, layer, pair_side):
        g = layer.parent

        if self.use_selection:
            return g

        if pair_side == 'left':
            g = Glyphs.font.glyphs[layer.parent.rightKerningGroup] or layer.parent
        if pair_side == 'right':
            g = Glyphs.font.glyphs[layer.parent.leftKerningGroup] or layer.parent

        if g is None:
            g = layer.parent

        return g

    @staticmethod
    def make_list_unique(this_list):
        unique_list = []
        for x in this_list:
            if x in unique_list or x is None:
                continue
            unique_list.append(x)

        return unique_list

    def get_categorised_glyphs(self, side):
        # cats = defaultdict(lambda: defaultdict(list))
        cats = dict((k, defaultdict(list)) for k in self.all_kern_categories)
        for g in [x for x in Glyphs.font.glyphs if self.is_elligable(x)]:
            l = cats.get(g.category, cats.get(g.subCategory, cats['Other']))
            l[g.script].append(self.get_canonincal_kerning_glyph(g.layers[0], side))

        for cat in cats.keys():
            for script in cats[cat].keys():
                cats[cat][script] = self.make_list_unique(cats[cat][script])

        return cats

    def get_string(self, left_g, right_g):
        string = None

        if self.category == 'Quotes':
            cat = left_g.subCategory if left_g.subCategory != 'Other' else left_g.category
            pattern = _kerningStrings.patterns.get(left_g.script, _kerningStrings.patterns.get('latin')).get(cat + '-Quotes', '')
            strings = [pattern.format(right=right_g.name, left=left_g.name, qL=quote_pair[0], qR=quote_pair[1]).replace(' /', '/') for quote_pair in _kerningStrings.quotations]
            string = '  '.join(strings)

        if not string and self.use_real:
            base_name_left, _, suffix_left = left_g.name.partition('.')
            base_name_right, _, suffix_right = right_g.name.partition('.')

            potentials = [
                base_name_left + base_name_right,
                base_name_left + '/' + base_name_right,
                '/' + base_name_left + ' ' + base_name_right,
                '/' + base_name_left + '/' + base_name_right,
            ]
            for s in potentials:
                string = _kerningStrings.strings.get(s)
                if string:
                    break
                print(s)

        if not string:
            pattern = self.get_pattern(left_g, right_g)
            string = pattern.format(right=right_g.name, left=left_g.name).replace(' /', '/')

        if not string:
            string = '/' + left_g.name + '/' + right_g.name

        return string

    def get_category_for_glyph(self, glyph):
        if glyph.category in self.all_kern_categories:
            return glyph.category

        if glyph.subCategory in self.all_kern_categories:
            return glyph.subCategory

        if glyph.subCategory == 'Currancy':
            return 'Number'

        return 'Other'

    def get_pattern(self, main_glyph, other_glyph):
        scripts_patterns = _kerningStrings.patterns.get(main_glyph.script, {})
        # print(self.get_category_for_glyph(main_glyph))
        # print(self.get_category_for_glyph(main_glyph) + '-' + self.get_category_for_glyph(other_glyph), self.all_kern_categories)
        pattern = scripts_patterns.get(self.get_category_for_glyph(main_glyph) + '-' + self.get_category_for_glyph(other_glyph), '')

        if self.category == 'Number':
            suffix = ''.join(main_glyph.name.partition('.')[1:])
        else:
            suffix = ''

        try:
            pattern = pattern.format(
                suffix=suffix,
                left='{left}',
                right='{right}',
            )
        except KeyError:
            pass

        return pattern

    def is_elligable(self, glyph, side='both'):
        if self.ignore_red and glyph.color == 0:
            return False

        if not glyph.export:
            return False

        for vgn in self.verboten[side]:
            if re.match(vgn.replace('.', '\\.').replace('*', '.*'), glyph.name):
                return False
        return True

    def makeitso(self, sender):
        try:
            self.w.close()
        except AttributeError:
            pass
        self.category = self.all_kern_categories[self.w.radioCategories.get()]

        self.use_real = self.w.use_real.get()
        self.use_selection = self.w.use_selected.get()
        self.ignore_red = self.w.ignore_red.get()
        all_strings = []

        if self.category == 'Quotes':
            left_of_string_glyphs = self.make_list_unique([self.get_canonincal_kerning_glyph(sl, 'right') for sl in Glyphs.font.selectedLayers if self.is_elligable(sl.parent, 'right')])
            right_of_string_glyphs = self.make_list_unique([self.get_canonincal_kerning_glyph(sl, 'left') for sl in Glyphs.font.selectedLayers if self.is_elligable(sl.parent, 'left')])
            pairs = zip_longest(left_of_string_glyphs, right_of_string_glyphs)
            for p in pairs:
                gl, gr = p
                if gl is None:
                    gl = gr if gr in left_of_string_glyphs else left_of_string_glyphs[0]
                if gr is None:
                    gr = gl if gl in left_of_string_glyphs else right_of_string_glyphs[0]

                kerning_string = self.get_string(gl, gr)
                if kerning_string not in all_strings:
                    all_strings.append(kerning_string)

        else:
            # Holds kerning key glyphs that have been seen already, to avoid duplicates
            processed_main_glyphs_left = OrderedDict()
            processed_main_glyphs_right = OrderedDict()
            # print([(k, self.categories_rightside[k].keys()) for k in self.categories_rightside.keys()])
            for sl in Glyphs.font.selectedLayers:
                # Process the selected glyph on the left side
                main_g_left = self.get_canonincal_kerning_glyph(sl, 'left')
                pair_strings_left = []
                if self.is_elligable(main_g_left, 'left'):
                    if main_g_left.name not in processed_main_glyphs_left.keys():
                        processed_main_glyphs_left[main_g_left.name] = [sl.parent.name]
                        try:
                            if sl.parent.script:
                                other_glyphs_rightside = self.categories_rightside[self.category].get(sl.parent.script, self.categories_rightside[self.category].get(None))
                            else:
                                other_glyphs_rightside = self.categories_rightside[self.category].get(None, self.categories_rightside[self.category].get('latin'))
                        except KeyError:
                            other_glyphs_rightside = []
                        # print(self.category, self.categories_rightside.keys())
                        print(sl.parent.script, self.category, self.categories_rightside[self.category].keys())

                        for g in other_glyphs_rightside:
                            if not self.is_elligable(g, 'right'):
                                continue
                            other_g = self.get_canonincal_kerning_glyph(g.layers[sl.associatedMasterId], 'right')
                            kerning_string_left = self.get_string(main_g_left, other_g)
                            if kerning_string_left not in pair_strings_left:
                                pair_strings_left.append(kerning_string_left)
                    else:
                        processed_main_glyphs_left[main_g_left.name].append(sl.parent.name)

                    if pair_strings_left:
                        pair_strings_left.insert(0, main_g_left.name)

                # Process the selected glyph on the right side
                main_g_right = self.get_canonincal_kerning_glyph(sl, 'right')
                pair_strings_right = []
                if self.is_elligable(main_g_right, 'right'):
                    if main_g_right.name not in processed_main_glyphs_right.keys():
                        processed_main_glyphs_right[main_g_right.name] = [sl.parent.name]

                        if self.category == 'Quotes':
                            other_glyphs_leftside = [main_g_right]
                            main_g_right = self.get_canonincal_kerning_glyph(sl, 'left')
                        else:
                            if sl.parent.script:
                                other_glyphs_leftside = self.categories_leftside[self.category].get(sl.parent.script, self.categories_leftside[self.category].get(None, []))
                            else:
                                other_glyphs_leftside = self.categories_leftside[self.category].get(None, self.categories_leftside[self.category].get('latin', []))

                        for g in other_glyphs_leftside:
                            if not self.is_elligable(g, 'left'):
                                continue
                            other_g = self.get_canonincal_kerning_glyph(g.layers[sl.associatedMasterId], 'left')
                            kerning_string_right = self.get_string(other_g, main_g_right)
                            if kerning_string_right not in pair_strings_right:
                                pair_strings_right.append(kerning_string_right)
                    else:
                        processed_main_glyphs_right[main_g_right.name].append(sl.parent.name)

                    if pair_strings_right:
                        pair_strings_right.insert(0, main_g_right.name)

                left_string = '  '.join(self.make_list_unique(pair_strings_left))
                right_string = '  '.join(self.make_list_unique(pair_strings_right))
                if all([left_string, right_string]):
                    pair_strings = '\n'.join([left_string, right_string])
                else:
                    pair_strings = left_string or right_string

                # print(':', pair_strings, ':')

                if pair_strings:
                    all_strings.append(pair_strings)

        Glyphs.font.newTab('\n\n'.join(all_strings))
        Glyphs.font.currentTab.previewInstances = 'live'
        Glyphs.font.currentTab.scale = 0.065
        Glyphs.font.currentTab.textCursor = 3
        Glyphs.font.tool = 'TextTool'
コード例 #10
0
class copyMetrics(object):
    def __init__(self):
        item_height = 24.0
        w_width = 500.0
        w_height = item_height * 12
        margin = 10
        next_y = margin
        col_1_width = w_width - (margin * 2)
        item_height = 24

        self.get_prefs('copyMetricsFromFont.pref')

        self.available_layers = dict(
            ('{} - {}'.format(os.path.basename(font.filepath), master), master)
            for font in Glyphs.fonts for master in font.masters)

        self.w = Window((w_width, w_height), "Copy Metrics")

        self.w.hText_2 = TextBox((margin, next_y, col_1_width, item_height),
                                 "Copy metrics from this font/layer:",
                                 sizeStyle='regular')
        next_y += item_height
        self.w.available_layers = PopUpButton(
            (margin, next_y, col_1_width, item_height),
            sorted(self.available_layers.keys()
                   ))  # , callback=self.update_brace_value)
        next_y += item_height + margin
        metrics_options = [
            'Left sidebearing Only',
            'Right sidebearing Only',
            'Width Only (keep LSB)',
            'Width Only (assign proportionally)',
            "Left sidebearing and Width",
            "Left sidebearing and Right sidebearing",
            'Width and Right sidebearing',
        ]
        self.w.metrics_to_copy = RadioGroup(
            (margin, next_y, col_1_width, item_height * len(metrics_options)),
            metrics_options)
        self.w.metrics_to_copy.set(int(self.prefs.get('metrics_to_copy', 0)))
        next_y += item_height * len(metrics_options) + margin

        self.w.gobutton = Button(
            (margin + (col_1_width / 4), next_y, col_1_width / 2, item_height),
            'Copy Metrics',
            callback=self.makeitso)
        next_y += item_height

        self.w.setDefaultButton(self.w.gobutton)
        self.w.open()

    def get_prefs(self, filename):
        self.pref_folder = os.path.expanduser(
            '~/Library/Application Support/Glyphs/Prefs')
        self.pref_filepath = os.path.join(self.pref_folder, filename)
        self.prefs = {}
        if os.path.exists(self.pref_filepath):
            with open(self.pref_filepath) as f:
                preflines = f.readlines()
            self.prefs = dict(
                line.split('\t') for line in preflines
                if line[0] != '#' and line.strip())

    def set_prefs(self, **kwargs):
        try:
            if not os.path.exists(self.pref_folder):
                os.makedirs(self.pref_folder)

            pref_string = '\n'.join(
                ['\t'.join(str(b) for b in a) for a in kwargs.items()])
            with open(self.pref_filepath, 'w') as f:
                f.write(pref_string)
        except AttributeError:
            print('The Preference filename has not been set.')

    def makeitso(self, sender):
        metrics_to_copy_mode = self.w.metrics_to_copy.get()

        self.set_prefs(metrics_to_copy=metrics_to_copy_mode)

        selected_layer_name = self.w.available_layers.getItem()
        source_master = self.available_layers[selected_layer_name]

        for l in Glyphs.font.selectedLayers:
            gname = l.parent.name
            corresponding_glyph = source_master.font.glyphs[gname]
            if corresponding_glyph is None:
                continue

            corresponding_layer = corresponding_glyph.layers[source_master.id]

            print(l.LSB, l.width, l.RSB)
            print(corresponding_layer.LSB, corresponding_layer.width,
                  corresponding_layer.RSB)

            if metrics_to_copy_mode == 0:
                l.LSB = corresponding_layer.LSB

            elif metrics_to_copy_mode == 1:
                l.RSB = corresponding_layer.RSB

            elif metrics_to_copy_mode == 2:
                l.width = corresponding_layer.width

            elif metrics_to_copy_mode == 3:
                diff = (corresponding_layer.width - l.width) / 2
                l.LSB += diff
                l.width = corresponding_layer.width

            elif metrics_to_copy_mode == 4:
                l.LSB = corresponding_layer.LSB
                l.width = corresponding_layer.width

            elif metrics_to_copy_mode == 5:
                l.LSB = corresponding_layer.LSB
                l.RSB = corresponding_layer.RSB

            elif metrics_to_copy_mode == 6:
                l.RSB = corresponding_layer.RSB
                l.width = corresponding_layer.width
            print(l.LSB, l.width, l.RSB)

        self.w.close()
コード例 #11
0
class makePlist(object):
    def __init__(self):
        self.file_name = 'CustomFilter Project Glyph Sets.plist'
        folder_path = os.path.dirname(Glyphs.font.filepath)
        self.file_path = os.path.join(folder_path, self.file_name)

        self.charsets = OrderedDict()
        self.parse_plist()

        self.basic_xml = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.08">
<array>
{charsets}
</array>
</plist>"""

        item_height = 24.0
        w_width = 350.0
        w_height = item_height * 7
        margin = 10
        next_y = margin
        col_1_width = w_width - (margin * 2)
        item_height = 24

        self.w = Window((w_width, w_height), "Insert a font as brace layers")

        next_y = margin
        self.w.charset_name_label = TextBox((margin, next_y + 2, col_1_width, item_height), "Character Set Name:", sizeStyle='regular')
        next_y += item_height
        self.w.charset_name = EditText((margin, next_y, col_1_width, item_height), callback=self.check_extant_charsets)
        next_y += item_height + 2
        self.w.extant_warning = TextBox((margin, next_y + 2, col_1_width, item_height), "The Character Set already exists and will be overwritten!", sizeStyle='small')
        self.w.extant_warning.show(False)
        next_y += (item_height * 0.7) + margin

        self.w.reopen = CheckBox((margin, next_y, col_1_width, item_height), "Close and Reopen current file", value=True)
        next_y += item_height + margin

        self.w.gobutton = Button((margin + (col_1_width / 4), next_y, col_1_width / 2, item_height), 'Make Character Set', callback=self.makeitso)

        self.w.setDefaultButton(self.w.gobutton)

        self.w.open()

    class charset:
        def __init__(self, xml_string=None):
            self.name = None
            self.glyph_names = None

            self.charset_template = """
\t<dict>
\t\t<key>list</key>
\t\t<array>
\t\t\t{glyphnames}
\t\t</array>
\t\t<key>name</key>
\t\t<string>{charset_name}</string>
\t</dict>"""

            if xml_string:
                self.parse_xml_string(xml_string)

        def parse_xml_string(self, xml_string):
            xml_string = re.sub('<!--.+?-->', '', xml_string)
            try:
                self.name = re.search(r'<key>name<\/key>\s+<string>(.+)<\/string>', xml_string).group(1)

                glyph_names_string = re.search(r'<array>(.+?)<\/array>', xml_string, re.DOTALL).group(1)
                self.glyph_names = re.findall(r'<string>(.+?)<\/string>', glyph_names_string)
            except AttributeError:
                pass

        def make_xml_string(self):
            glyph_strings = '\n'.join(['\t\t\t<string>{gn}</string>'.format(gn=gn) for gn in self.glyph_names])
            return self.charset_template.format(glyphnames=glyph_strings, charset_name=self.name)

    def parse_plist(self):
        if not os.path.exists(self.file_path):
            self.make_file()

        with open(self.file_path, 'r') as f:
            file_contents = f.read()

        all_charsets = re.findall(r'<dict>.*?<\/dict>', file_contents, re.DOTALL)
        for cs in all_charsets:
            csObj = self.charset(cs)
            self.charsets[csObj.name] = csObj

    def check_extant_charsets(self, sender):
        self.w.extant_warning.show(sender.get() in self.charsets.keys())

    def make_file(self):
        with open(self.file_path, 'w') as f:
            f.write(self.basic_xml.format(charsets=''))

    def check_file(self):
        with open(self.file_path, 'r') as f:
            file_contents = f.read()
        return file_contents.startswith('<?xml version="1.0" encoding="UTF-8"?>')

    def replace_charset(self, charset_name, charset_entry):
        with open(self.file_path, 'r') as f:
            file_contents = f.read()
        all_char_sets = re.findall(r'<dict>.*?<\/dict>', file_contents, re.DOTALL)
        try:
            this_char_set = [x for x in all_char_sets if '<string>{}</string>'.format(charset_name) in x][0]
        except IndexError:
            return None

        file_contents.replace(this_char_set, charset_entry)
        with open(self.file_path, 'w') as f:
            f.write(file_contents)

        return True

    def append_charset(self, full_entry):
        with open(self.file_path, 'r') as f:
            file_contents = f.read()

        full_entry = full_entry + '\n</array>'
        file_contents = full_entry.join(file_contents.rsplit('</array>', 1))
        with open(self.file_path, 'w') as f:
            f.write(file_contents)

    def makeitso(self, sender):
        self.w.close()
        charset_name = self.w.charset_name.get()
        if not charset_name:
            return

        if not os.path.exists(self.file_path):
            self.make_file()

        if not self.check_file():
            self.make_file()

        glyph_names = [l.parent.name for l in Glyphs.font.selectedLayers]

        this_charset = self.charsets.get(charset_name) or self.charset()
        this_charset.name = charset_name
        this_charset.glyph_names = glyph_names
        self.charsets[charset_name] = this_charset

        all_charset_string = '\n'.join([cs.make_xml_string() for cs in self.charsets.values()])
        full_text = self.basic_xml.format(charsets=all_charset_string)

        with open(self.file_path, 'w') as f:
            f.write(full_text)

        if self.w.reopen.get():
            this_filepath = str(Glyphs.font.filepath)
            Glyphs.font.close()
            Glyphs.open(this_filepath)
コード例 #12
0
from vanilla import Window, SquareButton, TextEditor
import vanilla
import mojo

def onSubmit(sender): 
    CurrentFont().info.note = window.textEditor.get()
    window.close()

if CurrentFont() is None:
    vanilla.dialogs.message("there is no current font, operation aborted")
else:
    window = Window((400, 400),"edit info.note", minSize=(100, 100))
    window.textEditor = TextEditor(posSize=(0, 0, 400, 300))
    noteContent = CurrentFont().info.note or ""
    window.textEditor.set(noteContent)
    window.updateNoteButton = SquareButton(posSize=(0, 350, 100, 50), title="update", callback=onSubmit)
    window.cancelButton = SquareButton(posSize=(110, 350, 100, 50), title="cancel", callback=lambda x: window.close())
    window.open()
コード例 #13
0
class GlyphnameDialog( object):

	def __init__( self ):
		x = 10
		y = 10
		height = 20
		button_width = 30
		glyphname_width = 180
		gap = 6
		self.w = Window( ( x + button_width + gap + glyphname_width + gap + button_width + x, y + height + y ), "insert glyph" )
		self.w.center()
		self.w.glyphname = EditText( ( x, y, glyphname_width, height ), '')
		x += glyphname_width + gap
		self.w.alignleft = Button( ( x, y, button_width, height ), LEFT, callback = self.buttonCallback )
		x += button_width + gap
		self.w.alignright = Button( ( x, y, button_width, height ), RIGHT, callback = self.buttonCallback )
		self.w.setDefaultButton( self.w.alignleft )
		self.w.alignright.bind( "\x1b", [] )
		self.w.open()

	def buttonCallback( self, sender ):
		title = sender.getTitle()
		glyphname = self.w.glyphname.get()
		if not glyphname:
			self.w.close()
			return
		if len( glyphname ) == 1:
			uni = ord(glyphname)
			g = font.glyphForUnicode_("%.4X" % uni)
			if g:
				glyphname = g.name
		other_glyph = font.glyphs[ glyphname ]
		if not other_glyph:
			for glyph in font.glyphs:
				if glyph.name.startswith( glyphname ):
					other_glyph = glyph
					print 'Using', glyph.name
					break
			else:
				print 'No matching glyph found.'
				self.w.close()
				return
		
		for layer in font.selectedLayers:
			glyph = layer.parent
			glyph.beginUndo()
			# deselect all
			for path in layer.paths:
				for node in path.nodes:
					layer.removeObjectFromSelection_( node )
			# find other layer
			for other_layer in other_glyph.layers:
				if other_layer.name == layer.name:
					# insert paths
					for path in other_layer.copyDecomposedLayer().paths:
						if title == RIGHT:
							shift = layer.width - other_layer.width
							for node in path.nodes:
								node.x = node.x + shift
						layer.paths.append( path )
						# select path
						layer.paths[-1].selected = True
					break
			glyph.endUndo()
		self.w.close()
コード例 #14
0
ファイル: copy_glyphs.py プロジェクト: roboDocs/copyGlyphs
class CopyGlyphs:

    def __init__(self):
        self.doMarkGlyphs = 0
        self.doOverwrite = 1
        self.sourceFontList = AllFonts()
        self.destinationFontList = AllFonts()
        self.source_font = self.sourceFontList[0]
        self.destination_fonts = None
        self.glyphs = None
        self.mark = NSColor.redColor()

        sl = []
        for f in self.sourceFontList:
            if f.info.familyName != None:
                fn = f.info.familyName
            else:
                fn = "None"
            if f.info.styleName != None:
                fs = f.info.styleName
            else:
                fs = "None"
            sl.append(fn+" "+fs)

        ## create a window
        self.w = Window((700, 500), "Copy Glyphs", minSize=(700, 500))
        self.w.sourceTitle = TextBox((15, 20, 200, 20), "Source Font:")
        self.w.sourceFont = PopUpButton((15, 42, -410, 20), sl, callback=self.sourceCallback)
        self.w.glyphs = GlyphCollectionView((16, 70, -410, -65), initialMode="list", enableDelete=False, allowDrag=False, selectionCallback=self.glyphCallback)
        self._sortGlyphs(self.source_font)
        self.w.desTitle = TextBox((-400, 20, 200, 20), "Destination Fonts:")
        self.w.destinationFonts = FontList((-400, 42, -15, -115), self.destinationFontList, selectionCallback=self.desCallback)
        self.w.overwrite = CheckBox((-395, -105, 130, 22), "Overwrite glyphs", callback=self.overwriteCallback, value=self.doOverwrite)
        self.w.markGlyphs = CheckBox((-395, -84, 100, 22), "Mark Glyphs", callback=self.markCallback, value=self.doMarkGlyphs)
        self.w.copyButton = Button((-115, -40, 100, 20), 'Copy Glyphs', callback=self.copyCallback)
        self.w.line = HorizontalLine((10, -50, -10, 1))
        self._checkSelection()
        self._updateDest()
        ## open the window
        self.w.open()

    def _updateDest(self):
        des = list(self.sourceFontList)
        des.remove(self.source_font)
        self.w.destinationFonts.set(des)

    def _sortGlyphs(self, font):
        gs = font.keys()        
        self.w.glyphs.set([font[x] for x in sorted(gs)])

    def _altName(self, font, glyph):
        name = glyph + '.copy'
        count = 1
        while name in font.keys():
            name = name + str(count)
            count += 1
        return name


    def _checkSelection(self):
        if self.glyphs == None or len(self.glyphs) == 0:
            if len(self.source_font.selection) != 0:
                self.glyphs = self.source_font.selection
                select = []
                for i, g in enumerate(self.w.glyphs):
                    if g.name in self.glyphs:
                        select.append(i)
                print(select)
                self.w.glyphs.setSelection(select)
        print(self.glyphs)


    def copyGlyphs(self, glyphs, source_font, destination_fonts, overwrite, mark):
        for glyph in glyphs:
            for font in destination_fonts:
                if glyph in font.keys() and overwrite == 0:
                    n = self._altName(font, glyph)
                else:
                    n = glyph

                font.insertGlyph(source_font[glyph], name=n)

                if mark == 1:
                    font[n].mark = NSColorToRgba(self.mark)

    def overwriteCallback(self, sender):
        self.doOverwrite = sender.get()

    def markCallback(self, sender):
        self.doMarkGlyphs = sender.get()
        if self.doMarkGlyphs == 1:
            self.w.colorWell = ColorWell((-265, -85, 100, 23), callback=self.colorCallback, color=self.mark)
        else:
            del self.w.colorWell

    def colorCallback(self, sender):
        self.mark = sender.get()

    def sourceCallback(self, sender):
        self.source_font = self.sourceFontList[sender.get()]
        self._sortGlyphs(self.source_font)
        self._checkSelection()
        self._updateDest()

    def glyphCallback(self, sender):
        self.glyphs = [self.w.glyphs[x].name for x in sender.getSelection()]

    def desCallback(self, sender):
        self.destination_fonts = [sender.get()[x] for x in sender.getSelection()]

    def copyCallback(self, sender):
        self.sheet = Sheet((300, 50), self.w)
        self.sheet.bar = ProgressBar((10, 20, -10, 10), isIndeterminate=True, sizeStyle="small")
        self.sheet.open()
        self.sheet.bar.start()
        self.copyGlyphs(self.glyphs, self.source_font, self.destination_fonts, self.doOverwrite, self.doMarkGlyphs)
        self.sheet.bar.stop()
        self.sheet.close()
        del self.sheet
        self.w.close()
コード例 #15
0
class GlyphnameDialog(object):
    def __init__(self):
        x = 10
        y = 10
        height = 20
        button_width = 30
        glyphname_width = 180
        gap = 6
        self.w = Window(
            (x + button_width + gap + glyphname_width + gap + button_width + x,
             y + height + y), "insert glyph")
        self.w.center()
        self.w.glyphname = EditText((x, y, glyphname_width, height), '')
        x += glyphname_width + gap
        self.w.alignleft = Button((x, y, button_width, height),
                                  LEFT,
                                  callback=self.buttonCallback)
        x += button_width + gap
        self.w.alignright = Button((x, y, button_width, height),
                                   RIGHT,
                                   callback=self.buttonCallback)
        self.w.setDefaultButton(self.w.alignleft)
        self.w.alignright.bind("\x1b", [])
        self.w.open()

    def buttonCallback(self, sender):
        alignment = sender.getTitle()
        glyphname = self.w.glyphname.get()
        if not glyphname:
            self.w.close()
            return
        if len(glyphname) == 1:
            uni = ord(glyphname)
            g = font.glyphForUnicode_("%.4X" % uni)
            if g:
                glyphname = g.name
        other_glyph = font.glyphs[glyphname]
        if not other_glyph:
            for glyph in font.glyphs:
                if glyph.name.startswith(glyphname):
                    other_glyph = glyph
                    print 'Using', glyph.name
                    break
            else:
                print 'No matching glyph found.'
                self.w.close()
                return

        selected_glyphs = set([layer.parent for layer in font.selectedLayers])

        for glyph in selected_glyphs:
            glyph.beginUndo()
            for layer in glyph.layers:
                # find other layer
                for other_layer in other_glyph.layers:
                    if other_layer.name == layer.name:
                        insert_paths(layer, other_layer, alignment)
                        break
                else:
                    if active_layerId == layer.layerId:
                        insert_paths(
                            layer,
                            other_glyph.layers[layer.associatedMasterId],
                            alignment)
            glyph.endUndo()
        self.w.close()
コード例 #16
0
class replaceNamedComponent:
    def __init__(self):
        item_height = 24.0
        margin = 10
        w_width = 350.0
        w_height = (item_height * 5) + margin
        next_y = margin
        col_1_width = w_width - (margin * 2)
        item_height = 24

        self.get_prefs('SlantComponentPositions.pref')

        self.w = Window((w_width, w_height), "Slant Angle")

        self.w.slant_angle_text = TextBox(
            (margin, next_y + 2, col_1_width, item_height),
            "Slant Angle:",
            sizeStyle='regular')
        next_y += item_height
        self.w.slant_angle = EditText(
            (margin, next_y, col_1_width, item_height),
            self.prefs.get('slant_angle', ''))
        next_y += item_height + margin

        self.w.slant_all_layers = CheckBox(
            (margin, next_y, col_1_width, item_height),
            "Slant All Layers",
            value=int(self.prefs.get('slant_all_layers')),
            sizeStyle='regular')
        next_y += item_height + margin

        self.w.makeitso = Button(
            (w_width / 4.0, next_y, col_1_width / 2.0, item_height),
            'Slant Components',
            callback=self.makeitso)
        self.w.setDefaultButton(self.w.makeitso)

        self.w.open()
        self.w.center()

    def get_prefs(self, filename):
        self.pref_folder = os.path.expanduser(
            '~/Library/Application Support/Glyphs/Prefs')
        self.pref_filepath = os.path.join(self.pref_folder, filename)
        self.prefs = {}
        if os.path.exists(self.pref_filepath):
            with open(self.pref_filepath) as f:
                preflines = f.readlines()
            self.prefs = dict(
                line.split('\t') for line in preflines
                if line[0] != '#' and line.strip())

    def set_prefs(self, **kwargs):
        try:
            if not os.path.exists(self.pref_folder):
                os.makedirs(self.pref_folder)

            pref_string = '\n'.join(
                ['\t'.join(str(b) for b in a) for a in kwargs.items()])
            with open(self.pref_filepath, 'w') as f:
                f.write(pref_string)
        except AttributeError:
            print('The Preference filename has not been set.')

    def get_reference_point(self, selection, layer=None):
        return self.get_center_of_selection(selection)
        # return NSPoint(self.get_obj_center(layer).x, Glyphs.font.selectedFontMaster.xHeight / 2)

    def get_obj_center(self, obj):
        return NSPoint(obj.bounds.origin.x + (obj.bounds.size.width / 2),
                       obj.bounds.origin.y + (obj.bounds.size.height / 2))

    def get_center_of_selection(self, selection):
        all_points = []
        for obj in selection:
            try:
                all_points.append(self.get_obj_center(obj))
            except AttributeError:
                all_points.append(obj.position)

        all_xs = [x.x for x in all_points]
        all_ys = [x.x for x in all_points]

        x_average = sum(all_xs) / len(all_xs)
        y_average = sum(all_ys) / len(all_ys)

        return NSPoint(int(x_average), int(y_average))

    def makeitso(self, sender):
        self.w.close()
        slant_all_layers = self.w.slant_all_layers.get()
        # print(slant_all_layers, type(slant_all_layers))
        try:
            slant_angle = float(self.w.slant_angle.get())
        except TypeError:
            Glyphs.showNotification('Slant Component Positions',
                                    'The slant angle was not a number!')
            return

        self.set_prefs(slant_angle=slant_angle,
                       slant_all_layers=slant_all_layers)

        if not Glyphs.font.selectedLayers:
            return

        for sl in Glyphs.font.selectedLayers:
            if slant_all_layers:
                layers = [l for l in sl.parent.layers]
            else:
                layers = [sl]

            for l in layers:
                comps = [c for c in l.components if c.selected
                         ] or [c for c in l.components]
                if not comps:
                    Glyphs.showNotification(
                        'Slant Component Positions',
                        'You haven\'t selected any Components!')
                    return

                reference_point = self.get_reference_point(comps, l)
                for c in comps:
                    x_shift = math.tan(math.radians(slant_angle)) * (
                        self.get_obj_center(c).y - reference_point.y)
                    c.position = NSPoint(c.position.x + x_shift, c.position.y)
コード例 #17
0
class KerningController(BaseWindowController):
    """this is the main controller of TT kerning editor, it handles different controllers and dialogues with font data"""

    # these attributes take good care of undo/redo stack
    archive = []
    recordIndex = 0

    displayedWord = ''
    displayedPairs = []
    activePair = None

    canvasScalingFactor = CANVAS_SCALING_FACTOR_INIT

    fontsOrder = None
    navCursor_X = 0  # related to pairs
    navCursor_Y = 0  # related to active fonts

    isPreviewOn = False
    areVerticalLettersDrawn = True
    areGroupsShown = True
    areCollisionsShown = False
    isKerningDisplayActive = True
    isSidebearingsActive = True
    isCorrectionActive = True
    isMetricsActive = True
    isColorsActive = True
    prevGraphicsValues = None

    isSymmetricalEditingOn = False
    isSwappedEditingOn = False
    isVerticalAlignedEditingOn = False

    autoSave = True
    autoSaveSpan = 5  # mins

    kerningLogger = None

    def __init__(self):
        super(KerningController, self).__init__()
        # init time for the autosave
        self.initTime = datetime.now()
        # init logging
        self._initLogger()
        # init fonts
        if AllFonts() == []:
            message(
                'No fonts, no party!',
                'Please, open some fonts before starting the mighty MultiFont Kerning Controller'
            )
            return None

        self.w = Window((0, 0, PLUGIN_WIDTH, PLUGIN_HEIGHT),
                        PLUGIN_TITLE,
                        minSize=(PLUGIN_WIDTH, PLUGIN_HEIGHT))
        self.w.bind('resize', self.mainWindowResize)

        # load opened fonts
        self.initFontsOrder()

        # exception window (will appear only if needed)
        self.exceptionWindow = ChooseExceptionWindow(
            ['group2glyph', 'glyph2group', 'glyph2glyph'],
            callback=self.exceptionWindowCallback)
        self.exceptionWindow.enable(False)

        # jump to line window (will appear only if invoked)
        self.jumpToLineWindow = JumpToLineWindow(
            callback=self.jumpToLineWindowCallback)
        self.jumpToLineWindow.enable(False)

        self.jumping_Y = MARGIN_VER
        self.jumping_X = MARGIN_HOR
        self.w.wordListController = WordListController(
            (self.jumping_X, self.jumping_Y, LEFT_COLUMN, 260),
            callback=self.wordListControllerCallback)
        self.displayedWord = self.w.wordListController.get()

        self.jumping_Y += self.w.wordListController.getPosSize()[3] + 4
        self.w.word_font_separationLine = HorizontalLine(
            (self.jumping_X, self.jumping_Y, LEFT_COLUMN,
             vanillaControlsSize['HorizontalLineThickness']))

        self.jumping_Y += MARGIN_VER
        fontsOrderControllerHeight = FONT_ROW_HEIGHT * len(
            self.fontsOrder) + MARGIN_HOR
        self.w.fontsOrderController = FontsOrderController(
            (self.jumping_X, self.jumping_Y, LEFT_COLUMN,
             fontsOrderControllerHeight),
            self.fontsOrder,
            callback=self.fontsOrderControllerCallback)
        self.jumping_Y += fontsOrderControllerHeight
        self.w.fonts_controller_separationLine = HorizontalLine(
            (self.jumping_X, self.jumping_Y, LEFT_COLUMN,
             vanillaControlsSize['HorizontalLineThickness']))

        self.jumping_Y += MARGIN_VER
        self.w.joystick = JoystickController(
            (self.jumping_X, self.jumping_Y, LEFT_COLUMN, 348),
            fontObj=self.fontsOrder[self.navCursor_Y],
            isSymmetricalEditingOn=self.isSymmetricalEditingOn,
            isSwappedEditingOn=self.isSwappedEditingOn,
            isVerticalAlignedEditingOn=self.isVerticalAlignedEditingOn,
            autoSave=self.autoSave,
            autoSaveSpan=self.autoSaveSpan,
            activePair=None,
            callback=self.joystickCallback)

        self.jumping_Y += self.w.joystick.getPosSize()[3] + MARGIN_VER
        self.w.graphicsManager = GraphicsManager(
            (self.jumping_X, -202, LEFT_COLUMN, 202),
            isKerningDisplayActive=self.isKerningDisplayActive,
            areVerticalLettersDrawn=self.areVerticalLettersDrawn,
            areGroupsShown=self.areGroupsShown,
            areCollisionsShown=self.areCollisionsShown,
            isSidebearingsActive=self.isSidebearingsActive,
            isCorrectionActive=self.isCorrectionActive,
            isMetricsActive=self.isMetricsActive,
            isColorsActive=self.isColorsActive,
            callback=self.graphicsManagerCallback)

        self.jumping_X += LEFT_COLUMN + MARGIN_COL * 2
        self.jumping_Y = MARGIN_VER

        self.w.displayedWordCaption = TextBox(
            (self.jumping_X, self.jumping_Y, 200,
             vanillaControlsSize['TextBoxRegularHeight']), self.displayedWord)

        self.w.scalingFactorController = FactorController(
            (-MARGIN_HOR - 72, self.jumping_Y, 72,
             vanillaControlsSize['TextBoxRegularHeight']),
            self.canvasScalingFactor,
            callback=self.scalingFactorControllerCallback)

        self.jumping_Y += self.w.displayedWordCaption.getPosSize(
        )[3] + MARGIN_COL
        self.initWordDisplays()
        self.w.joystick.setActivePair(
            self.getActiveWordDisplay().getActivePair())

        # observers!
        addObserver(self, 'openCloseFontCallback', "fontDidOpen")
        addObserver(self, 'openCloseFontCallback', "fontDidClose")
        self.setUpBaseWindowBehavior()
        self.w.open()

    def _initLogger(self):
        # create a logger
        self.kerningLogger = logging.getLogger('kerningLogger')
        self.kerningLogger.setLevel(logging.INFO)
        # create file handler which logs info messages
        fileHandle = logging.FileHandler('kerningLogger.log')
        fileHandle.setLevel(logging.INFO)
        # create console handler with a higher log level, only errors
        consoleHandler = logging.StreamHandler()
        consoleHandler.setLevel(logging.ERROR)
        # create formatter and add it to the handlers
        formatter = logging.Formatter(u'%(asctime)s – %(message)s')
        fileHandle.setFormatter(formatter)
        consoleHandler.setFormatter(formatter)
        # add the handlers to the logger
        self.kerningLogger.addHandler(fileHandle)
        self.kerningLogger.addHandler(consoleHandler)

    def windowCloseCallback(self, sender):
        removeObserver(self, "fontDidOpen")
        removeObserver(self, "fontWillClose")
        self.exceptionWindow.close()
        super(KerningController, self).windowCloseCallback(sender)
        self.w.close()

    def openCloseFontCallback(self, sender):
        self.deleteWordDisplays()
        self.initFontsOrder()
        self.w.fontsOrderController.setFontsOrder(self.fontsOrder)
        self.initWordDisplays()

        fontsOrderControllerHeight = FONT_ROW_HEIGHT * len(
            self.fontsOrder) + MARGIN_HOR
        prevFontsOrderPos = self.w.fontsOrderController.getPosSize()
        self.w.fontsOrderController.setPosSize(
            (prevFontsOrderPos[0], prevFontsOrderPos[1], prevFontsOrderPos[2],
             fontsOrderControllerHeight))

        prevSepLinePos = self.w.fonts_controller_separationLine.getPosSize()
        self.w.fonts_controller_separationLine.setPosSize(
            (prevSepLinePos[0],
             prevFontsOrderPos[1] + fontsOrderControllerHeight,
             prevSepLinePos[2], prevSepLinePos[3]))

        prevJoystickPos = self.w.joystick.getPosSize()
        self.w.joystick.setPosSize(
            (prevJoystickPos[0],
             prevFontsOrderPos[1] + fontsOrderControllerHeight + MARGIN_VER,
             prevJoystickPos[2], prevJoystickPos[3]))

    def initFontsOrder(self):
        if self.fontsOrder is None:
            fontsOrder = [f for f in AllFonts() if f.path is not None]
            self.fontsOrder = sorted(fontsOrder,
                                     key=lambda f: os.path.basename(f.path))
        else:
            newFontsOrder = [f for f in AllFonts() if f in self.fontsOrder] + [
                f for f in AllFonts() if f not in self.fontsOrder
            ]
            self.fontsOrder = newFontsOrder

        for eachFont in self.fontsOrder:
            status, report = checkGroupConflicts(eachFont)
            if status is False:
                self.kerningLogger.error('groups conflict in {}'.format(
                    eachFont.path))
                self.kerningLogger.error(report)

    def deleteWordDisplays(self):
        for eachI in xrange(len(self.fontsOrder)):
            try:
                delattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1))
                self.jumping_Y = MARGIN_VER + vanillaControlsSize[
                    'TextBoxRegularHeight']
            except Exception as e:
                self.kerningLogger.error(traceback.format_exc())

    def initWordDisplays(self):
        windowWidth, windowHeight = self.w.getPosSize()[2], self.w.getPosSize(
        )[3]
        netTotalWindowHeight = windowHeight - MARGIN_COL - MARGIN_VER * 2 - vanillaControlsSize[
            'TextBoxRegularHeight'] - MARGIN_HOR * (len(self.fontsOrder) - 1)

        try:
            singleWindowHeight = netTotalWindowHeight / len(self.fontsOrder)
        except ZeroDivisionError:
            singleWindowHeight = 0

        rightColumnWidth = windowWidth - LEFT_COLUMN - MARGIN_COL

        self.jumping_Y = MARGIN_VER + vanillaControlsSize[
            'TextBoxRegularHeight'] + MARGIN_COL
        for eachI in xrange(len(self.fontsOrder)):

            if eachI == self.navCursor_Y:
                initPairIndex = self.navCursor_X
            else:
                initPairIndex = None

            try:
                wordCtrl = WordDisplay(
                    (self.jumping_X, self.jumping_Y, rightColumnWidth,
                     singleWindowHeight),
                    displayedWord=self.displayedWord,
                    canvasScalingFactor=self.canvasScalingFactor,
                    fontObj=self.fontsOrder[eachI],
                    isKerningDisplayActive=self.isKerningDisplayActive,
                    areVerticalLettersDrawn=self.areVerticalLettersDrawn,
                    areGroupsShown=self.areGroupsShown,
                    areCollisionsShown=self.areCollisionsShown,
                    isSidebearingsActive=self.isSidebearingsActive,
                    isCorrectionActive=self.isCorrectionActive,
                    isMetricsActive=self.isMetricsActive,
                    isColorsActive=self.isColorsActive,
                    isSymmetricalEditingOn=self.isSymmetricalEditingOn,
                    isSwappedEditingOn=self.isSwappedEditingOn,
                    indexPair=initPairIndex)

            except Exception:
                self.kerningLogger.error(traceback.format_exc())

            self.jumping_Y += singleWindowHeight + MARGIN_HOR
            setattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1), wordCtrl)

    def updateWordDisplays(self):
        for eachI in xrange(len(self.fontsOrder)):
            eachDisplay = getattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1))
            eachDisplay.setSymmetricalEditingMode(self.isSymmetricalEditingOn)
            eachDisplay.setSwappedEditingMode(self.isSwappedEditingOn)
            eachDisplay.setScalingFactor(self.canvasScalingFactor)
            eachDisplay.setGraphicsBooleans(
                self.isKerningDisplayActive, self.areVerticalLettersDrawn,
                self.areGroupsShown, self.areCollisionsShown,
                self.isSidebearingsActive, self.isCorrectionActive,
                self.isMetricsActive, self.isColorsActive)
            eachDisplay.wordCanvasGroup.update()

    def nextWord(self, isRecording=True):
        self.w.wordListController.nextWord()
        self.displayedWord = self.w.wordListController.get()
        self.isSwappedEditingOn = False
        self.w.joystick.setSwappedEditing(self.isSwappedEditingOn)
        self.updateEditorAccordingToDiplayedWord()
        if isRecording is True:
            self.appendRecord('nextWord')

    def previousWord(self, isRecording=True):
        self.w.wordListController.previousWord()
        self.displayedWord = self.w.wordListController.get()
        self.isSwappedEditingOn = False
        self.w.joystick.setSwappedEditing(self.isSwappedEditingOn)
        self.updateEditorAccordingToDiplayedWord()
        if isRecording is True:
            self.appendRecord('previousWord')

    def jumpToLine(self, lineIndex, isRecording=True):
        if isRecording is True:
            self.appendRecord(
                'jumpToLine',
                (self.w.wordListController.getActiveIndex(), lineIndex))

        self.w.wordListController.jumpToLine(lineIndex)
        self.displayedWord = self.w.wordListController.get()
        self.isSwappedEditingOn = False
        self.w.joystick.setSwappedEditing(self.isSwappedEditingOn)
        self.updateEditorAccordingToDiplayedWord()

    def oneStepGroupSwitch(self, location):
        if self.isVerticalAlignedEditingOn is True:
            for eachI in xrange(len(self.fontsOrder)):
                eachDisplay = getattr(self.w,
                                      'wordCtrl_{:0>2d}'.format(eachI + 1))
                eachDisplay.switchGlyphFromGroup(location, self.navCursor_X)
        else:
            self.getActiveWordDisplay().switchGlyphFromGroup(
                location, self.navCursor_X)
        self.w.joystick.setActivePair(
            self.getActiveWordDisplay().getActivePair())

    def updateEditorAccordingToDiplayedWord(self):
        self.w.displayedWordCaption.set(self.displayedWord)
        if len(self.displayedWord) - 1 < (self.navCursor_X + 1):
            self.navCursor_X = len(self.displayedWord) - 2
        for eachI in xrange(len(self.fontsOrder)):
            eachDisplay = getattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1))

            if self.isVerticalAlignedEditingOn is False:
                if eachI == self.navCursor_Y:
                    eachDisplay.setActivePairIndex(self.navCursor_X)
            else:
                eachDisplay.setActivePairIndex(self.navCursor_X)

            eachDisplay.setDisplayedWord(self.displayedWord)
        self.updateWordDisplays()

        self.getActiveWordDisplay().setActivePairIndex(self.navCursor_X)
        self.w.joystick.setActivePair(
            self.getActiveWordDisplay().getActivePair())

    def getActiveWordDisplay(self):
        return getattr(self.w, 'wordCtrl_{:0>2d}'.format(self.navCursor_Y + 1))

    def setGraphicsManagerForPreviewMode(self):

        self.prevGraphicsValues = (self.isKerningDisplayActive,
                                   self.areVerticalLettersDrawn,
                                   self.areGroupsShown,
                                   self.areCollisionsShown,
                                   self.isSidebearingsActive,
                                   self.isCorrectionActive,
                                   self.isMetricsActive, self.isColorsActive)

        # set preview mode variables
        self.isKerningDisplayActive = True
        self.areVerticalLettersDrawn = False
        self.areGroupsShown = False
        self.areCollisionsShown = False
        self.isSidebearingsActive = False
        self.isCorrectionActive = False
        self.isMetricsActive = False
        self.isColorsActive = False
        self.w.graphicsManager.set(
            self.isKerningDisplayActive, self.areVerticalLettersDrawn,
            self.areGroupsShown, self.areCollisionsShown,
            self.isSidebearingsActive, self.isCorrectionActive,
            self.isMetricsActive, self.isColorsActive)

    def restoreGraphicsManagerValues(self):
        self.isKerningDisplayActive, self.areVerticalLettersDrawn, self.areGroupsShown, self.areCollisionsShown, self.isSidebearingsActive, self.isCorrectionActive, self.isMetricsActive, self.isColorsActive = self.prevGraphicsValues
        self.prevGraphicsValues = None
        self.w.graphicsManager.set(
            self.isKerningDisplayActive, self.areVerticalLettersDrawn,
            self.areGroupsShown, self.areCollisionsShown,
            self.isSidebearingsActive, self.isCorrectionActive,
            self.isMetricsActive, self.isColorsActive)

    def switchPreviewAttribute(self, isRecording=True):
        if self.isPreviewOn is True:
            self.isPreviewOn = False
            self.restoreGraphicsManagerValues()
            self.w.graphicsManager.switchControls(True)
        else:
            self.isPreviewOn = True
            self.setGraphicsManagerForPreviewMode()
            self.w.graphicsManager.switchControls(False)
        self.updateWordDisplays()

        self.updateWordDisplays()
        if isRecording is True:
            self.appendRecord('preview')

    def switchSolvedAttribute(self, isRecording=True):
        self.w.wordListController.switchActiveWordSolvedAttribute()
        if isRecording is True:
            self.appendRecord('solved')

    def switchSwappedEditing(self, isRecording=True):
        self.isSwappedEditingOn = not self.isSwappedEditingOn
        if self.isSwappedEditingOn is True and self.isSymmetricalEditingOn is True:
            self.isSymmetricalEditingOn = False
            self.w.joystick.setSymmetricalEditing(self.isSymmetricalEditingOn)
        self.updateWordDisplays()
        if isRecording is True:
            self.appendRecord('swappedEditing')

    def switchSymmetricalEditing(self, isRecording=True):
        self.isSymmetricalEditingOn = not self.isSymmetricalEditingOn
        if self.isSwappedEditingOn is True and self.isSymmetricalEditingOn is True:
            self.isSwappedEditingOn = False
            self.w.joystick.setSwappedEditing(self.isSwappedEditingOn)
        self.updateWordDisplays()
        if isRecording is True:
            self.appendRecord('symmetricalEditing')

    def switchVerticalAlignedEditing(self, isRecording=True):
        self.isVerticalAlignedEditingOn = not self.isVerticalAlignedEditingOn
        for eachI in xrange(len(self.fontsOrder)):
            eachDisplay = getattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1))
            if self.isVerticalAlignedEditingOn is True:
                eachDisplay.setActivePairIndex(self.navCursor_X)
            else:
                if eachI != self.navCursor_Y:
                    eachDisplay.setActivePairIndex(None)
        self.updateWordDisplays()
        if isRecording is True:
            self.appendRecord('verticalAlignedEditing')

    def exceptionTrigger(self):
        activeFont = self.fontsOrder[self.navCursor_Y]
        selectedPair = self.getActiveWordDisplay().getActivePair()
        correction, kerningReference, pairKind = getCorrection(
            selectedPair, activeFont)
        isException, doesExists, parentPair = isPairException(
            kerningReference, activeFont)

        # delete exception
        if isException:
            if self.isVerticalAlignedEditingOn is True:
                selectedFonts = self.fontsOrder
            else:
                selectedFonts = [self.fontsOrder[self.navCursor_Y]]

            for eachFont in selectedFonts:
                isException, doesExists, parentPair = isPairException(
                    kerningReference, eachFont)
                if isException:
                    deletePair(kerningReference, eachFont)
                    self.appendRecord('deletePair',
                                      (kerningReference, eachFont, correction))

        else:
            if not correction:
                # set standard pair to zero
                self.setPairCorrection(0)
                correction, kerningReference, pairKind = getCorrection(
                    selectedPair, activeFont)
                isException, doesExists, parentPair = isPairException(
                    kerningReference, activeFont)

            # trigger exception window
            exceptionOptions = possibleExceptions(selectedPair,
                                                  kerningReference, activeFont)
            if len(exceptionOptions) == 1:
                self.exceptionWindow.set(exceptionOptions[0])
                self.exceptionWindow.trigger()
            elif len(exceptionOptions) > 1:
                self.exceptionWindow.setOptions(exceptionOptions)
                self.exceptionWindow.enable(True)
            else:
                self.showMessage(
                    'no possible exceptions',
                    'kerning exceptions can be triggered only starting from class kerning corrections'
                )

        self.updateWordDisplays()

    # manipulate data
    def deletePair(self, isRecording=True):
        if self.autoSave is True:
            self.checkAutoSave()

        selectedPair = self.getActiveWordDisplay().getActivePair()
        if self.isVerticalAlignedEditingOn is True:
            selectedFonts = self.fontsOrder
        else:
            selectedFonts = [self.fontsOrder[self.navCursor_Y]]

        for eachFont in selectedFonts:
            previousAmount, kerningReference, pairKind = getCorrection(
                selectedPair, eachFont)
            deletePair(kerningReference, eachFont)
            if isRecording is True:
                self.appendRecord('deletePair',
                                  (kerningReference, eachFont, previousAmount))
            self.kerningLogger.info(
                DELETE_PAIR_LOG.format(leftGlyphName=selectedPair[0],
                                       rightGlyphName=selectedPair[1],
                                       familyName=eachFont.info.familyName,
                                       styleName=eachFont.info.styleName))

            if self.isSwappedEditingOn is True:
                swappedCorrectionKey = selectedPair[1], selectedPair[0]
                previousAmount, kerningReference, pairKind = getCorrection(
                    swappedCorrectionKey, eachFont)
                deletePair(kerningReference, eachFont)
                if isRecording is True:
                    self.appendRecord('deletePair', kerningReference, eachFont,
                                      previousAmount)
                self.kerningLogger.info(
                    DELETE_PAIR_LOG.format(leftGlyphName=kerningReference[0],
                                           rightGlyphName=kerningReference[1],
                                           familyName=eachFont.info.familyName,
                                           styleName=eachFont.info.styleName))

            if self.isSymmetricalEditingOn is True:
                symmetricalCorrectionKey = findSymmetricalPair(selectedPair)
                previousAmount, kerningReference, pairKind = getCorrection(
                    symmetricalCorrectionKey, eachFont)
                deletePair(kerningReference, eachFont)
                if isRecording is True:
                    self.appendRecord('deletePair', kerningReference, eachFont,
                                      previousAmount)
                self.kerningLogger.info(
                    DELETE_PAIR_LOG.format(leftGlyphName=kerningReference[0],
                                           rightGlyphName=kerningReference[1],
                                           familyName=eachFont.info.familyName,
                                           styleName=eachFont.info.styleName))

        self.updateWordDisplays()

    def setPairCorrection(self, amount, isRecording=True):
        if self.autoSave is True:
            self.checkAutoSave()

        selectedPair = self.getActiveWordDisplay().getActivePair()
        if self.isVerticalAlignedEditingOn is True:
            selectedFonts = self.fontsOrder
        else:
            selectedFonts = [self.fontsOrder[self.navCursor_Y]]

        for eachFont in selectedFonts:
            if isRecording is True:
                previousAmount = getCorrection(selectedPair, eachFont)[0]
                self.appendRecord('setCorrection',
                                  (selectedPair, eachFont, previousAmount))
            setCorrection(selectedPair, eachFont, amount)
            self.kerningLogger.info(
                SET_CORRECTION_LOG.format(leftGlyphName=selectedPair[0],
                                          rightGlyphName=selectedPair[1],
                                          familyName=eachFont.info.familyName,
                                          styleName=eachFont.info.styleName,
                                          amount=amount))

            if self.isSwappedEditingOn is True:
                swappedCorrectionKey = selectedPair[1], selectedPair[0]
                if isRecording is True:
                    previousAmount = getCorrection(swappedCorrectionKey,
                                                   eachFont)[0]
                    self.appendRecord(
                        'setCorrection',
                        (swappedCorrectionKey, eachFont, previousAmount))
                setCorrection(swappedCorrectionKey, eachFont, amount)
                self.kerningLogger.info(
                    SET_CORRECTION_LOG.format(
                        leftGlyphName=swappedCorrectionKey[0],
                        rightGlyphName=swappedCorrectionKey[1],
                        familyName=eachFont.info.familyName,
                        styleName=eachFont.info.styleName,
                        amount=amount))

            if self.isSymmetricalEditingOn is True:
                symmetricalCorrectionKey = findSymmetricalPair(selectedPair)
                if symmetricalCorrectionKey:
                    if isRecording is True:
                        previousAmount = getCorrection(
                            symmetricalCorrectionKey, eachFont)[0]
                        self.appendRecord('setCorrection',
                                          (symmetricalCorrectionKey, eachFont,
                                           previousAmount))
                    setCorrection(symmetricalCorrectionKey, eachFont, amount)
                    self.kerningLogger.info(
                        SET_CORRECTION_LOG.format(
                            leftGlyphName=symmetricalCorrectionKey[0],
                            rightGlyphName=symmetricalCorrectionKey[1],
                            familyName=eachFont.info.familyName,
                            styleName=eachFont.info.styleName,
                            amount=amount))

        self.updateWordDisplays()

    def modifyPairCorrection(self, amount, isRecording=True):
        if self.autoSave is True:
            self.checkAutoSave()

        selectedPair = self.getActiveWordDisplay().getActivePair()

        if self.isVerticalAlignedEditingOn is True:
            selectedFonts = self.fontsOrder
        else:
            selectedFonts = [self.fontsOrder[self.navCursor_Y]]

        for eachFont in selectedFonts:
            correction, correctionKey, pairKind = getCorrection(
                selectedPair, eachFont)
            correction = 0 if correction is None else correction
            if isRecording is True:
                previousAmount = getCorrection(selectedPair, eachFont)[0]
                self.appendRecord('setCorrection',
                                  (selectedPair, eachFont, previousAmount))
            setCorrection(selectedPair, eachFont, correction + amount)
            self.kerningLogger.info(
                SET_CORRECTION_LOG.format(leftGlyphName=selectedPair[0],
                                          rightGlyphName=selectedPair[1],
                                          familyName=eachFont.info.familyName,
                                          styleName=eachFont.info.styleName,
                                          amount=amount))

            if self.isSwappedEditingOn is True:
                swappedPair = selectedPair[1], selectedPair[0]
                if isRecording is True:
                    previousAmount = getCorrection(selectedPair, eachFont)[0]
                    self.appendRecord('setCorrection',
                                      (swappedPair, eachFont, previousAmount))
                setCorrection(swappedPair, eachFont, correction + amount)
                self.kerningLogger.info(
                    SET_CORRECTION_LOG.format(
                        leftGlyphName=swappedPair[0],
                        rightGlyphName=swappedPair[1],
                        familyName=eachFont.info.familyName,
                        styleName=eachFont.info.styleName,
                        amount=amount))

            if self.isSymmetricalEditingOn is True:
                symmetricalCorrectionKey = findSymmetricalPair(selectedPair)
                if symmetricalCorrectionKey:
                    if isRecording is True:
                        previousAmount = getCorrection(
                            symmetricalCorrectionKey, eachFont)[0]
                        self.appendRecord('setCorrection',
                                          (symmetricalCorrectionKey, eachFont,
                                           previousAmount))
                    setCorrection(symmetricalCorrectionKey, eachFont,
                                  correction + amount)
                    self.kerningLogger.info(
                        SET_CORRECTION_LOG.format(
                            leftGlyphName=symmetricalCorrectionKey[0],
                            rightGlyphName=symmetricalCorrectionKey[1],
                            familyName=eachFont.info.familyName,
                            styleName=eachFont.info.styleName,
                            amount=amount))

        self.w.joystick.updateCorrectionValue()
        self.updateWordDisplays()

    # cursor methods
    def cursorLeftRight(self, direction, isRecording=True):
        assert direction in ['left', 'right']

        if direction == 'left':
            step = -1
            if isRecording is True:
                self.appendRecord('cursorLeft')
        else:
            step = +1
            if isRecording is True:
                self.appendRecord('cursorRight')

        self.navCursor_X = (self.navCursor_X +
                            step) % (len(self.displayedWord) - 1)
        for eachI in xrange(len(self.fontsOrder)):
            eachDisplay = getattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1))
            if self.isVerticalAlignedEditingOn is False:
                if eachI == self.navCursor_Y:
                    eachDisplay.setActivePairIndex(self.navCursor_X)
            else:
                eachDisplay.setActivePairIndex(self.navCursor_X)
        self.w.joystick.setActivePair(
            self.getActiveWordDisplay().getActivePair())
        self.updateWordDisplays()

    def cursorUpDown(self, direction, isRecording=True):
        assert direction in ['up', 'down']
        if direction == 'up':
            step = -1
            if isRecording is True:
                self.appendRecord('cursorUp')
        else:
            step = +1
            if isRecording is True:
                self.appendRecord('cursorDown')

        if self.isVerticalAlignedEditingOn is False:
            self.getActiveWordDisplay().setActivePairIndex(None)  # old
            self.navCursor_Y = (self.navCursor_Y + step) % len(self.fontsOrder)
            self.getActiveWordDisplay().setActivePairIndex(
                self.navCursor_X)  # new
            self.w.joystick.setFontObj(self.fontsOrder[self.navCursor_Y])
            self.updateWordDisplays()

    ### callbacks
    def exceptionWindowCallback(self, sender):
        if self.isVerticalAlignedEditingOn is False:
            selectedFonts = [self.fontsOrder[self.navCursor_Y]]
        else:
            selectedFonts = self.fontsOrder
        selectedPair = self.getActiveWordDisplay().getActivePair()
        if sender.lastEvent == 'submit':
            for indexFont, eachFont in enumerate(selectedFonts):
                exceptionKey = sender.get()
                correction, kerningReference, pairKind = getCorrection(
                    selectedPair, eachFont)
                setRawCorrection(exceptionKey, eachFont, correction)
                self.appendRecord('createException',
                                  (exceptionKey, eachFont, correction))
                if indexFont == self.navCursor_Y:
                    self.w.joystick.updateCorrectionValue()
            self.updateWordDisplays()

    def jumpToLineWindowCallback(self, sender):
        if sender.get() is not None:
            self.jumpToLine(sender.get())
        self.jumpToLineWindow.enable(False)

    def mainWindowResize(self, mainWindow):
        windowWidth, windowHeight = mainWindow.getPosSize(
        )[2], mainWindow.getPosSize()[3]
        rightColumnWidth = windowWidth - LEFT_COLUMN

        # caption
        prevdisplayedWordCaptionSize = self.w.displayedWordCaption.getPosSize()
        self.w.displayedWordCaption.setPosSize(
            (prevdisplayedWordCaptionSize[0], prevdisplayedWordCaptionSize[1],
             rightColumnWidth, prevdisplayedWordCaptionSize[3]))

        # displayers
        initY = MARGIN_VER + vanillaControlsSize[
            'TextBoxRegularHeight'] + MARGIN_COL
        netTotalWindowHeight = windowHeight - initY - MARGIN_VER - MARGIN_HOR * (
            len(self.fontsOrder) - 1)

        try:
            singleWordDisplayHeight = netTotalWindowHeight / len(
                self.fontsOrder)
        except ZeroDivisionError:
            singleWordDisplayHeight = 0

        y = initY
        for eachI in xrange(len(self.fontsOrder)):
            eachDisplay = getattr(self.w, 'wordCtrl_{:0>2d}'.format(eachI + 1))
            eachDisplay.adjustSize(
                (self.jumping_X, y, rightColumnWidth, singleWordDisplayHeight))
            eachDisplay.setCtrlSize(rightColumnWidth, singleWordDisplayHeight)
            y += singleWordDisplayHeight + MARGIN_HOR

    def scalingFactorControllerCallback(self, sender):
        self.canvasScalingFactor = sender.getScalingFactor()
        self.updateWordDisplays()

    def wordListControllerCallback(self, sender):
        self.displayedWord = sender.get()
        self.updateEditorAccordingToDiplayedWord()

    def fontsOrderControllerCallback(self, sender):
        self.deleteWordDisplays()
        self.fontsOrder = []
        for indexFont, eachFont in enumerate(sender.getFontsOrder()):
            if sender.getIsDisplayedOrder()[indexFont] is True:
                self.fontsOrder.append(eachFont)
        self.initWordDisplays()

    def graphicsManagerCallback(self, sender):
        self.isKerningDisplayActive, self.areVerticalLettersDrawn, self.areGroupsShown, self.areCollisionsShown, self.isSidebearingsActive, self.isCorrectionActive, self.isMetricsActive, self.isColorsActive = sender.get(
        )
        self.updateWordDisplays()

    def joystickCallback(self, sender):
        joystickEvent = sender.getLastEvent()
        assert joystickEvent in JOYSTICK_EVENTS

        if joystickEvent == 'minusMajor':
            if self.isKerningDisplayActive is True:
                self.modifyPairCorrection(-MAJOR_STEP)
            else:
                self.showMessage('Be aware!',
                                 KERNING_NOT_DISPLAYED_ERROR,
                                 callback=None)

        elif joystickEvent == 'minusMinor':
            if self.isKerningDisplayActive is True:
                self.modifyPairCorrection(-MINOR_STEP)
            else:
                self.showMessage('Be aware!',
                                 KERNING_NOT_DISPLAYED_ERROR,
                                 callback=None)

        elif joystickEvent == 'plusMinor':
            if self.isKerningDisplayActive is True:
                self.modifyPairCorrection(MINOR_STEP)
            else:
                self.showMessage('Be aware!',
                                 KERNING_NOT_DISPLAYED_ERROR,
                                 callback=None)

        elif joystickEvent == 'plusMajor':
            if self.isKerningDisplayActive is True:
                self.modifyPairCorrection(MAJOR_STEP)
            else:
                self.showMessage('Be aware!',
                                 KERNING_NOT_DISPLAYED_ERROR,
                                 callback=None)

        elif joystickEvent == 'preview':
            self.switchPreviewAttribute()

        elif joystickEvent == 'solved':
            self.switchSolvedAttribute()
            self.nextWord()

        elif joystickEvent == 'symmetricalEditing':
            self.switchSymmetricalEditing()

        elif joystickEvent == 'swappedEditing':
            self.switchSwappedEditing()

        elif joystickEvent == 'verticalAlignedEditing':
            self.switchVerticalAlignedEditing()

        elif joystickEvent == 'previousWord':
            self.previousWord()

        elif joystickEvent == 'cursorUp':
            self.cursorUpDown('up')

        elif joystickEvent == 'cursorLeft':
            self.cursorLeftRight('left')

        elif joystickEvent == 'cursorRight':
            self.cursorLeftRight('right')

        elif joystickEvent == 'cursorDown':
            self.cursorUpDown('down')

        elif joystickEvent == 'nextWord':
            self.nextWord()

        elif joystickEvent == 'deletePair':
            if self.isKerningDisplayActive is True:
                self.deletePair()
                self.w.joystick.setActivePair(
                    self.getActiveWordDisplay().getActivePair())

        elif joystickEvent == 'switchLftGlyph':
            self.oneStepGroupSwitch(location='left')

        elif joystickEvent == 'switchRgtGlyph':
            self.oneStepGroupSwitch(location='right')

        elif joystickEvent == 'keyboardEdit':
            if self.isKerningDisplayActive is True:
                correctionAmount = self.w.joystick.getKeyboardCorrection()
                if correctionAmount is None:
                    self.deletePair()
                else:
                    self.setPairCorrection(correctionAmount)
                self.updateWordDisplays()
            else:
                self.showMessage('Be aware!',
                                 KERNING_NOT_DISPLAYED_ERROR,
                                 callback=None)
                self.w.joystick.updateCorrectionValue()

        elif joystickEvent == 'jumpToLineTrigger':
            self.jumpToLineWindow.enable(True)

        elif joystickEvent == 'exceptionTrigger':
            self.exceptionTrigger()

        # from here on events are not archived in undo/redo stack
        elif joystickEvent == 'undo':
            self.undo()

        elif joystickEvent == 'redo':
            self.redo()

        elif joystickEvent == 'autoSave':
            self.autoSave, self.autoSaveSpan = self.w.joystick.getAutoSaveState(
            )

    # autosaving fonts
    def checkAutoSave(self):
        justNow = datetime.now()
        if (justNow - self.initTime).seconds > self.autoSaveSpan * 60:
            self.saveFontsOrder()
            self.initTime = datetime.now()

    def saveFontsOrder(self):
        for eachFont in self.fontsOrder:
            eachFont.save()
        self.kerningLogger.info("all fonts saved")

    # undo/redo stack
    def appendRecord(self, actionName, data=None):
        if self.recordIndex < 0:
            self.archive = self.archive[:self.recordIndex]
            self.recordIndex = 0
        if data is None:
            self.archive.append(actionName)
        else:
            self.archive.append((actionName, data))

    def undo(self):
        if abs(self.recordIndex) <= len(self.archive) - 1:
            self.recordIndex -= 1
            self.pullRecordFromArchive('undo')

    def redo(self):
        if self.recordIndex < 0:
            self.recordIndex += 1
            self.pullRecordFromArchive('redo')

    def pullRecordFromArchive(self, direction):
        """we miss these methods: switchLftGlyph, switchRgtGlyph"""

        assert direction in ['redo', 'undo']
        if direction == 'redo':
            record = self.archive[
                self.recordIndex -
                1]  # othwerwise we won't get back to the initial status
        else:
            record = self.archive[self.recordIndex]

        # these records, we can simply invert (they related to events in UI)
        if isinstance(record, types.StringType) is True:
            if record == 'nextWord':
                if direction == 'undo':
                    self.previousWord(isRecording=False)
                else:
                    self.nextWord(isRecording=False)
            elif record == 'previousWord':
                if direction == 'undo':
                    self.nextWord(isRecording=False)
                else:
                    self.previousWord(isRecording=False)
            elif record == 'preview':
                self.switchPreviewAttribute(isRecording=False)
            elif record == 'solved':
                self.switchSolvedAttribute(isRecording=False)
            elif record == 'swappedEditing':
                self.switchSwappedEditing(isRecording=False)
            elif record == 'symmetricalEditing':
                self.switchSymmetricalEditing(isRecording=False)
            elif record == 'verticalAlignedEditing':
                self.switchVerticalAlignedEditing(isRecording=False)

            elif record == 'cursorLeft':
                if direction == 'undo':
                    self.cursorLeftRight('right', isRecording=False)
                else:
                    self.cursorLeftRight('left', isRecording=False)

            elif record == 'cursorRight':
                if direction == 'undo':
                    self.cursorLeftRight('left', isRecording=False)
                else:
                    self.cursorLeftRight('right', isRecording=False)

            elif record == 'cursorUp':
                if direction == 'undo':
                    self.cursorUpDown('down', isRecording=False)
                else:
                    self.cursorUpDown('up', isRecording=False)

            elif record == 'cursorDown':
                if direction == 'undo':
                    self.cursorUpDown('up', isRecording=False)
                else:
                    self.cursorUpDown('down', isRecording=False)

        # these relate to data manipulation...
        else:
            recordTitle, data = record

            if recordTitle == 'setCorrection':
                pair, font, amount = data
                setCorrection(pair, font, amount)
                self.updateWordDisplays()

            elif recordTitle == 'createException':
                pair, font, amount = data
                if direction == 'undo':
                    deletePair(pair, font)
                else:
                    setRawCorrection(pair, font, amount)

            elif recordTitle == 'deletePair':
                pair, font, amount = data
                if direction == 'undo':
                    setRawCorrection(pair, font, amount)
                else:
                    deletePair(pair, font)

            elif recordTitle == 'jumpToLine':
                previousIndex, nextIndex = data
                if direction == 'undo':
                    self.jumpToLine(previousIndex)
                else:
                    self.jumpToLine(nextIndex, isRecording=False)
コード例 #18
0
ファイル: copy_glyphs.py プロジェクト: davelab6/copyGlyphs
class CopyGlyphs:

    def __init__(self):
        self.doMarkGlyphs = 0
        self.doOverwrite = 1
        self.sourceFontList = AllFonts()
        self.destinationFontList = AllFonts()
        self.source_font = self.sourceFontList[0]
        self.destination_fonts = None
        self.glyphs = None
        self.mark = NSColor.redColor()
        
        ## create a window
        self.w = Window((700, 500), "Copy Glyphs", minSize=(700, 500))
        self.w.sourceTitle = TextBox((15, 20, 200, 20), "Source Font:")
        self.w.sourceFont = PopUpButton((15, 42, -410, 20), [f.info.familyName + ' ' + f.info.styleName for f in self.sourceFontList], callback=self.sourceCallback)
        self.w.glyphs = GlyphCollectionView((16, 70, -410, -65), initialMode="list", enableDelete=False, allowDrag=False, selectionCallback=self.glyphCallback)
        self._sortGlyphs(self.source_font)
        self.w.desTitle = TextBox((-400, 20, 200, 20), "Destination Fonts:")
        self.w.destinationFonts = FontList((-400, 42, -15, -115), self.destinationFontList, selectionCallback=self.desCallback)
        self.w.overwrite = CheckBox((-395, -105, 130, 22), "Overwrite glyphs", callback=self.overwriteCallback, value=self.doOverwrite)
        self.w.markGlyphs = CheckBox((-395, -84, 100, 22), "Mark Glyphs", callback=self.markCallback, value=self.doMarkGlyphs)
        self.w.copyButton = Button((-115, -40, 100, 20), 'Copy Glyphs', callback=self.copyCallback)
        self.w.line = HorizontalLine((10, -50, -10, 1))
        self._updateDest()
        ## open the window
        self.w.open()
    
    def _updateDest(self):
        des = list(self.sourceFontList)
        des.remove(self.source_font)
        self.w.destinationFonts.set(des)
    
    def _sortGlyphs(self, font):
        gs = font.keys()
        gs.sort()
        self.w.glyphs.set([font[x] for x in gs])

    def _altName(self, font, glyph):
        name = glyph + '.copy'
        count = 1
        while name in font.keys():
            name = name + str(count)
            count += 1
        return name
        
    def copyGlyphs(self, glyphs, source_font, destination_fonts, overwrite, mark):
        for glyph in glyphs:
            for font in destination_fonts:
                if glyph in font.keys() and overwrite == 0:
                    n = self._altName(font, glyph)
                else:
                    n = glyph
                
                font.insertGlyph(source_font[glyph], name=n)
                
                if mark == 1:
                    font[n].mark = NSColorToRgba(self.mark)

    def overwriteCallback(self, sender):
        self.doOverwrite = sender.get()

    def markCallback(self, sender):
        self.doMarkGlyphs = sender.get()
        if self.doMarkGlyphs == 1:
            self.w.colorWell = ColorWell((-265, -85, 100, 23), callback=self.colorCallback, color=self.mark)
        else:
            del self.w.colorWell
        
    def colorCallback(self, sender):
        self.mark = sender.get()
    
    def sourceCallback(self, sender):
        self.source_font = self.sourceFontList[sender.get()]
        self._sortGlyphs(self.source_font)
        self._updateDest()
 
    def glyphCallback(self, sender):
        self.glyphs = [self.w.glyphs[x].name for x in sender.getSelection()]
        
    def desCallback(self, sender):
        self.destination_fonts = [sender.get()[x] for x in sender.getSelection()]
        
    def copyCallback(self, sender):
        self.sheet = Sheet((300, 50), self.w)
        self.sheet.bar = ProgressBar((10, 20, -10, 10), isIndeterminate=True, sizeStyle="small")
        self.sheet.open()
        self.sheet.bar.start()
        self.copyGlyphs(self.glyphs, self.source_font, self.destination_fonts, self.doOverwrite, self.doMarkGlyphs)
        self.sheet.bar.stop()
        self.sheet.close()
        del self.sheet
        self.w.close()
コード例 #19
0
ファイル: LP_versioning.py プロジェクト: LaPolice/LP-RF-Tools
def userChecksInfoNote(font, fontState):
    window = Window((400, 400),"edit info.note", minSize=(100, 100))
    window.textEditor = TextEditor(posSize=(0, 0, 400, 300))
    noteContent = font.info.note or ""
    window.textEditor.set(noteContent)
    def handleCommit(s):
        editorContent = window.textEditor.get()
        window.close()
        commitInfoNote(editorContent, font, fontState)
    window.updateNoteButton = SquareButton(posSize=(0, 350, 100, 50), title="commit", callback=handleCommit)
    window.cancelButton = SquareButton(posSize=(110, 350, 130, 50), title="cancel operation", callback=lambda x: window.close())
    window.open()
コード例 #20
0
class makeItSo(object):
    def __init__(self):
        self.w = Window((550, 140), "Replace named Layer")
        self.w.editText = EditText((10, 15, -10, 22), placeholder="Layer Name", text='{170}')
        self.w.correct_path_direction = CheckBox((10, 50, -10, 18), "Correct Path Direction", value=True, sizeStyle='small')
        self.w.sync_metrics = CheckBox((210, 50, -10, 18), "Sync Metrics", value=True, sizeStyle='small')
        self.w.add_if_missing = CheckBox((10, 70, -10, 18), "Add layer if missing", value=True, sizeStyle='small')
        self.w.copybutton = Button((10, 100, -10, 17), "Replace layer", callback=self.buttonCallback)
        self.w.open()

    def buttonCallback(self, sender):
        self.w.close()

        target_font = Glyphs.font
        source_font = Glyphs.fonts[1]

        target_font.disableUpdateInterface()

        target_layer_name = self.w.editText.get()

        for source_glyph in source_font.glyphs:
            target_glyph = target_font.glyphs[source_glyph.name]
            if target_glyph is None:
                continue

            source_layer = source_glyph.layers[0]

            newL = copy.copy(source_layer)

            extant = True
            try:
                target_layer = [x for x in target_glyph.layers if x.name == target_layer_name][0]
                newL.associatedMasterId = target_layer.associatedMasterId
            except IndexError:
                if not self.w.add_if_missing.get():
                    continue
                source_weightValue = source_font.instances[0].weightValue
                mid = target_font.masters[0].id
                for m in target_font.masters:
                    if m.weightValue > source_weightValue:
                        break
                    mid = m.id
                newL.associatedMasterId = mid
                extant = False

            newL.name = target_layer_name
            for c in newL.components:
                c.automaticAlignment = False
                if target_font.glyphs[c.componentName] is None:
                    newLayerPaths = source_layer.copyDecomposedLayer()
                    newL.paths = newLayerPaths.paths
                    newL.components = []
                    break

            if self.w.correct_path_direction:
                newL.correctPathDirection()

            if extant:
                target_glyph.layers[target_layer.layerId] = newL
            else:
                target_glyph.layers.append(newL)

            if self.w.sync_metrics and extant:
                target_glyph.layers[target_layer.layerId].syncMetrics()

        target_font.enableUpdateInterface()
コード例 #21
0
class makeDisplay(object):
    def __init__(self):
        self.all_layer_combos = self.get_all_layer_combos()
        self.instance_values = self.get_instance_values()
        item_height = 24.0
        w_width = 300.0
        w_height = item_height * 10
        margin = 10
        next_y = margin
        col_1_width = w_width - (margin * 2)
        col_2_width = (w_width / 2) - (margin * 1.5)
        item_height = 24

        radio_height = 20 * 2
        self.get_prefs('addBraceLayers.pref')

        self.w = Window((w_width, w_height), "Add Layers")

        self.w.text_1 = TextBox((margin, next_y, col_1_width, item_height),
                                "Layer Combinations:",
                                sizeStyle='regular')
        next_y += item_height
        self.w.parent_layers = PopUpButton(
            (margin, next_y, col_1_width, item_height),
            self.all_layer_combos,
            sizeStyle='regular')
        self.set_all_layer_combos()
        next_y += item_height + margin

        self.w.brace_or_bracket = RadioGroup(
            (margin, next_y, col_1_width, radio_height),
            ['Bracket Layer [X]', 'Brace Layer {X}'],
            sizeStyle='regular')
        self.w.brace_or_bracket.set(int(self.prefs.get('brace_or_bracket', 0)))
        next_y += radio_height + margin

        self.w.text_2 = TextBox((margin, next_y, col_1_width, item_height),
                                "Layer Value:",
                                sizeStyle='regular')
        next_y += item_height
        self.w.layer_value = EditText(
            (margin, next_y, col_2_width, item_height),
            '',
            sizeStyle='small',
            placeholder='e.g. 700')
        self.w.instance_value_popup = PopUpButton(
            (margin + margin + col_2_width, next_y, col_2_width, item_height),
            self.instance_values,
            sizeStyle='regular',
            callback=self.changeinstance_value)
        next_y += item_height + margin
        if self.prefs.get('layer_value') is not None:
            self.w.layer_value.set(self.prefs.get('layer_value'))

        self.w.gobutton = Button(
            (margin + (col_1_width / 4), next_y, col_1_width / 2, item_height),
            'Add Layers',
            callback=self.makeitso)

        self.w.setDefaultButton(self.w.gobutton)
        self.w.center()
        self.w.open()
        # self.makeitso(None)

    def get_prefs(self, filename):
        self.pref_folder = os.path.expanduser(
            '~/Library/Application Support/Glyphs/Prefs')
        self.pref_filepath = os.path.join(self.pref_folder, filename)
        self.prefs = {}
        if os.path.exists(self.pref_filepath):
            with open(self.pref_filepath) as f:
                preflines = f.readlines()
            self.prefs = dict(
                line.split('\t') for line in preflines
                if line[0] != '#' and line.strip())

    def set_prefs(self, **kwargs):
        try:
            if not os.path.exists(self.pref_folder):
                os.makedirs(self.pref_folder)

            pref_string = '\n'.join(
                ['\t'.join(str(b) for b in a) for a in kwargs.items()])
            with open(self.pref_filepath, 'w') as f:
                f.write(pref_string)
        except AttributeError:
            print('The Preference filename has not been set.')

    def get_all_layer_combos(self):
        master_combos = OrderedDict()
        for mi, m in enumerate(Glyphs.font.masters):
            try:
                next_m = Glyphs.font.masters[mi + 1]
                if not next_m:
                    break
                combo_tup = (m, next_m)
                master_combos['{}-{}'.format(
                    *[x.name for x in combo_tup])] = combo_tup
            except IndexError:
                break

        return master_combos

    def set_all_layer_combos(self):
        selection_index = Glyphs.font.masterIndex
        if selection_index >= len(self.all_layer_combos):
            selection_index = len(self.all_layer_combos) - 1
        self.w.parent_layers.set(selection_index)

    def get_instance_values(self):
        return [''] + [
            ', '.join([str(x) for x in list(i.axes)])
            for i in Glyphs.font.instances
        ]

    def changeinstance_value(self, sender):
        self.w.layer_value.set(sender.getItem())

    def makeitso(self, sender):
        try:
            self.w.close()
        except AttributeError:
            pass

        parent_layers = self.w.parent_layers.getItem()
        brace_or_bracket = self.w.brace_or_bracket.get()
        layer_value = float(self.w.layer_value.get().strip())
        if int(layer_value) - layer_value == 0:
            layer_value = int(layer_value)

        self.set_prefs(
            parent_layers=parent_layers,
            brace_or_bracket=brace_or_bracket,
            layer_value=layer_value,
        )

        layer_name_template = '{master_name} {{{layer_value}}}' if brace_or_bracket else '{master_name} [{layer_value}]'

        master_names = parent_layers.split('-')
        masters = [m for m in Glyphs.font.masters if m.name in master_names]

        for sl in Glyphs.font.selectedLayers:
            g = sl.parent

            for m in masters:
                newL = copy.copy(g.layers[m.id])
                newL.layerId = None
                newL.associatedMasterId = m.id
                newL.name = layer_name_template.format(
                    master_name=m.name,
                    layer_value=layer_value,
                )
                g.layers.append(newL)