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()
Пример #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()
Пример #3
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()
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'
Пример #5
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)
Пример #6
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()
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)
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()
Пример #9
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()
Пример #10
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)
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()
Пример #12
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
Пример #13
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)
Пример #14
0
class ProofDrawer:
    def __init__(self, presetsList):
        self.fonts = ["Font 1", "Font 2"]
        self.presetsList = presetsList

        self.currentPreset = self.presetsList[0]
        self.presetNamesList = self._getPresetNames()

        # These 3 might be up for deletion
        self.proofGroupInspector = None
        self.editedGroupIndex = None
        self.listHasBeenEdited = False # A flag for later... see closeWindowCB()

        self._buildUI()
        self._refreshProofGroups()

        addObserver(self, "_inspectorClosed", "com.InspectorClosed")
        addObserver(self, "editProofGroupCB", "com.ProofGroupEdited")
        self.w.bind("close", self.closeWindowCB)

    def _buildUI(self):
        editPresetsImgPath = os.path.join(currentFileDir,\
                                          "..", "resources",\
                                          "editPresetsIcon.pdf")

        listForList = [
            {
                "title": "#",
                "key": "order",
                "width": 20,
                "editable": False
            },
            {
                "title": "Group name",
                "key": "name",
                "width": 160,
                "editable": True
            },
            {
                "title": "Type size",
                "key": "typeSize",
                "width": 70,
                "editable": True,
            },
            {
                "title": "Leading",
                "key": "leading",
                "width": 65,
                "editable": True
            },
            {
                "title": " 🖨",
                "key": "print",
                "cell": CheckBoxListCell()
            }
        ]

        width = 425
        left = 10
        row = 10
        textWidth = 48
        textHeight = 20
        popUpLeft = left + textWidth + 5
        presetsPopUpWidth = width - popUpLeft - 47
        listWidth = textWidth + presetsPopUpWidth

        self.w = Window((width, 600), "Proof Drawer")

        self.w.fontText = TextBox((left, row, textWidth, textHeight),
                                  "Font:",
                                  alignment="right")
        self.w.fontsList = PopUpButton((popUpLeft, row, -10, textHeight),
                                       items=self.fonts,
                                       callback=self.fontButtonCB)

        row += 30
        self.w.presetText = TextBox((left, row, textWidth, textHeight),
                                    "Preset:",
                                    alignment="right")

        self.w.presetsList = PopUpButton((popUpLeft, row, presetsPopUpWidth, textHeight),
                                         items=self.presetNamesList,
                                         callback=self.setCurrentPresetCB)

        self.w.editPresets = ImageButton((width - 38, row, 22, 22),
                                         imagePath=editPresetsImgPath,
                                         bordered=False,
                                         callback=self.openPresetsEditorCB)

        row += 35
        self.w.line1 = HorizontalLine((left, row, -10, 1))

        row += 15
        self.w.proofGroups = List((left + 3, row, listWidth, 255),
                                  rowHeight=18,
                                  items=[],
                                  columnDescriptions=listForList,
                                  allowsSorting=False,
                                  allowsMultipleSelection=False,
                                  editCallback=self.editProofGroupCB)

        buttonGroup1Left = popUpLeft + presetsPopUpWidth + 3
        buttonGroup1Top = row + 58
        self.w.inspectGroup = Button((buttonGroup1Left, buttonGroup1Top, 30, 20),
                                     "\u24D8",
                                     callback=self.inspectGroupCB)

        buttonGroup1Top += 40
        self.w.moveGroupUP = Button((buttonGroup1Left, buttonGroup1Top, 30, 20),
                                    "↑",
                                    callback=self.moveGroupCB)
        buttonGroup1Top += 25
        self.w.moveGroupDN = Button((buttonGroup1Left, buttonGroup1Top, 30, 20),
                                    "↓",
                                    callback=self.moveGroupCB)

        buttonGroup1Top += 40
        self.w.removeGroup = Button((buttonGroup1Left, buttonGroup1Top, 30, 20),
                                    "-",
                                    callback=self.removeGroupCB)

        row += 275
        self.w.line2 = HorizontalLine((left, row, -10, 1))

        row += 10
        self.w.additionalGroupNamesText = TextBox((left, row, -10, 20),
                                                  "Add more proof groups:")

        row += 25
        self.w.additionalGroupNames = List((left + 3, row, listWidth, 150),
                                           rowHeight=17,
                                           items=self.currentPreset.uniqueGroupNames,
                                           allowsSorting=False,
                                           allowsMultipleSelection=True)

        self.w.addGroup = Button((buttonGroup1Left, row + 60, 30, 20),
                                 "+",
                                 callback=self.addProofGroupsCB)

        buttonWidth = 100
        self.w.previewButton = Button((width/2 - buttonWidth - 10, -37, buttonWidth, 20),
                                      "Preview",
                                      callback=self.testerCB)

        self.w.printButton = Button((width/2 + 10, -37, buttonWidth, 20),
                                    "Print",
                                    callback=self.testerCB)
        self.w.setDefaultButton(self.w.printButton)

    def fontButtonCB(self, sender):
        selectedFont = self.fonts[sender.get()]
        self.w.setTitle("Proof Drawer: %s" % selectedFont)

    def openPresetsEditorCB(self, sender):
        presetsEditor = PresetsEditor(self.w, self.presetsList)
        presetsEditor.w.open()

    def setCurrentPresetCB(self, sender):
        selectedPresetIndex = sender.get()
        self.currentPreset = self.presetsList[selectedPresetIndex]
        self._refreshProofGroups()
        self.w.additionalGroupNames.set(self.currentPreset.uniqueGroupNames)

    def inspectGroupCB(self, sender):
        """
        Open new window that lets user inspect and further edit
        selected group.
        """
        if not self.w.proofGroups.getSelection():
            return

        editGroupIndex = self.w.proofGroups.getSelection()[0]
        selectedGroup = self.currentPreset.groups[editGroupIndex]

        self.proofGroupInspector = ProofGroupInspector(selectedGroup)
        self.proofGroupInspector.w.open()
        self.proofGroupInspector.w.center()
        self.proofGroupInspector.w.makeKey()
        self._uiEnabled(False)

    def editProofGroupCB(self, senderOrInfo):
        """
        Edit selected proof group and refresh proof groups list.
        senderOrInfo accepts callback sender or info from PostEvent

        ProofPreset.editGroup() is called on a field-by-field basis
        so we're not trying to edit "name" when only editing "leading"
        """
        # Don't do anything if proof groups aren't
        # ready to be edited or if nothing is selected
        if not self._proofReadyToEdit or\
        not self.w.proofGroups.getSelection():
            return

        selectedIndex = self.w.proofGroups.getSelection()[0]
        currentGroup = self.currentPreset.groups[selectedIndex]

        # Generate new dict to pass into ProofPreset.editGroup()
        propertiesToUpdate = {}
        for key, currentValue in currentGroup.items():
            # "info" coming back from Inspector
            if isinstance(senderOrInfo, dict) and senderOrInfo["editedProofGroup"]:
                newValue = senderOrInfo["editedProofGroup"][key]
            # "sender" coming back from List
            else:
                newValue = senderOrInfo[selectedIndex][key]

            if newValue != currentValue:
                propertiesToUpdate[key] = newValue

        try:
            self.currentPreset.editGroup(selectedIndex, propertiesToUpdate)
        except Exception as e:
            # Error handling here (some sort of warning window)
            print(e)

        self._refreshProofGroups(selectedIndex)

    def moveGroupCB(self, sender):
        """
        Move selected group and refresh proof groups list.

        Both up and down buttons call this method because they
        both call ProofPreset.moveGroup(currentIndex, new Index)
        """
        if not self.w.proofGroups or not self.w.proofGroups.getSelection():
            return

        direction = sender.getTitle()
        currentIndex = self.w.proofGroups.getSelection()[0]

        if direction == "↑":
            # First object can't move up
            if currentIndex == 0:
                return
            newIndex = currentIndex - 1
        else:
            # Last object can't move down
            if currentIndex == len(self.w.proofGroups) - 1:
                return
            newIndex = currentIndex + 1

        self.currentPreset.moveGroup(currentIndex, newIndex)
        self._refreshProofGroups(newSelection=newIndex)

    def removeGroupCB(self, sender):
        """
        Delete selected group and refresh proof groups list
        """
        if not self.w.proofGroups or not self.w.proofGroups.getSelection():
            return
        groupToDeleteIndex = self.w.proofGroups.getSelection()[0]

        self.currentPreset.removeGroup(groupToDeleteIndex)
        self._refreshProofGroups(newSelection=groupToDeleteIndex) # This will select next group

    def addProofGroupsCB(self, sender):
        """
        Add new groups from additionalGroupNames list
        """
        newGroupNamesIndices = self.w.additionalGroupNames.getSelection()
        if not newGroupNamesIndices:
            return

        for index in newGroupNamesIndices:
            groupToAdd = {"name": self.w.additionalGroupNames[index]}
            self.currentPreset.addGroup(groupToAdd)

        self._refreshProofGroups()

    def closeWindowCB(self, sender):
        """
        On close, save the state of the current preset.
        """
        # if self.proofGroupInspector:
        #     self.proofGroupInspector.w.close()

        # listToWrite = hf.convertToListOfPyDicts(self.w.proofGroups)

        # newPresetPath = os.path.join(currentFilePath, "..", "resources",\
        #                              "presets", "newTestPreset.json")
        # writeJSONpreset(newPresetPath, listToWrite)

        removeObserver(self, "com.InspectorClosed")
        removeObserver(self, "com.ProofGroupEdited")

    def testerCB(self, sender):
        """
        Use this for fake CB
        """
        print("hit: ", sender)

    def _getPresetNames(self):
        """
        Get preset names for display
        """
        return [preset.name for preset in self.presetsList]

    def _uiEnabled(self, onOff=True):
        """
        A master switch for all editable UI elements
        """
        self.w.proofGroups.enable(onOff)
        self.w.fontsList.enable(onOff)
        self.w.presetsList.enable(onOff)
        self.w.editPresets.enable(onOff)
        self.w.editPresets.enable(onOff)
        self.w.inspectGroup.enable(onOff)
        self.w.moveGroupUP.enable(onOff)
        self.w.moveGroupDN.enable(onOff)
        self.w.removeGroup.enable(onOff)
        self.w.additionalGroupNames.enable(onOff)
        self.w.addGroup.enable(onOff)

    def _inspectorClosed(self, info):
        """
        Prevent more than one inspector window
        from being opened.
        """
        self._uiEnabled(True)

    def _refreshProofGroups(self, newSelection=0):
        """
        Refresh the proof groups list, set order numbers,
        and set selection.

        newSelection defaults to first index in list.
        """
        # Set flag so editProofGroupCB() isn't
        # called when set proofGroups contents &
        # set order numbers
        self._proofReadyToEdit = False

        self.w.proofGroups.set(self.currentPreset.groups)

        newOrder = 1
        for item in self.w.proofGroups:
            item["order"] = newOrder
            newOrder += 1

        self._proofReadyToEdit = True
        self.w.proofGroups.setSelection([newSelection])