source: applicationScripts/FontLab/UFO/UFOCentral.py @ 2

Revision 2, 10.2 KB checked in by tal, 6 years ago (diff)
A handful of FontLab scripts.
Line 
1# FLM: UFO Central
2"""Export and import UFOs with metadata. Version 0.2"""
3
4import os
5import glob
6from robofab.pens.pointPen import AbstractPointPen
7from robofab.world import AllFonts, OpenFont, NewFont
8from robofab.interface.all.dialogs import GetFolder
9from dialogKit import *
10from FL import *
11import fl_cmd
12
13
14class InstructionPointPen(AbstractPointPen):
15
16    def __init__(self):
17        self._instructions = []
18
19    def beginPath(self):
20        d = {
21            "method":"beginPath"
22            }
23        self._instructions.append(d)
24
25    def endPath(self):
26        d = {
27            "method":"endPath"
28            }
29        self._instructions.append(d)
30
31    def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
32        d = {
33            "method":"addPoint",
34            "pt":pt,
35            }
36        if segmentType is not None:
37            d["segmentType"] = segmentType
38        if smooth is not None:
39            d["smooth"] = smooth
40        if name is not None:
41            d["name"] = name
42        self._instructions.append(d)
43
44    def addComponent(self, baseGlyphName, transformation):
45        d = {
46            "method":"addComponent",
47            "baseGlyphName":baseGlyphName,
48            "transformation":transformation
49            }
50
51    def getInstructions(self):
52        # filter out any single point contours (anchors)
53        instructions = []
54        pointStack = []
55        for instruction in self._instructions:
56            pointStack.append(instruction)
57            if instruction["method"] == "endPath":
58                if pointStack:
59                    if len(pointStack) > 3:
60                        instructions.extend(pointStack)
61                    pointStack = []
62        return instructions
63
64
65def _drawPointStack(stack, pointPen):
66    for instruction in stack:
67        meth = instruction["method"]
68        if meth == "beginPath":
69            pointPen.beginPath()
70        elif meth == "endPath":
71            pointPen.endPath()
72        elif meth == "addPoint":
73            pt = instruction["pt"]
74            smooth = instruction.get("smooth")
75            segmentType = instruction.get("segmentType")
76            name = instruction.get("name")
77            pointPen.addPoint(pt, segmentType, smooth, name)
78        elif meth == "addComponent":
79            baseGlyphName = instruction["baseGlyphName"]
80            transformation = instruction["transformation"]
81            pointPen.addComponent(baseGlyphName, transformation)
82        else:
83            raise NotImplementedError, meth
84
85def instructionsDrawPoints(instructions, pointPen):
86    """draw instructions created by InstructionPointPen"""
87    # filter out single point contours (anchors)
88    pointStack = []
89    for instruction in instructions:
90        pointStack.append(instruction)
91        meth = instruction["method"]
92        if meth == "endPath":
93            if len(pointStack) > 3:
94                _drawPointStack(pointStack, pointPen)
95            pointStack = []
96
97
98MASK_LIB_KEY = "org.robofab.fontlab.maskData"
99MARK_LIB_KEY = "org.robofab.fontlab.mark"
100
101
102def exportUFOWithMetaData(font, ufoPath=None, doMask=True, doMark=True, doProgress=False):
103    from robofab.pens.digestPen import DigestPointPen
104    # make sure that the font is the top most font
105    fl.ifont = font.fontIndex
106    # the mask
107    if doMask or doMark:
108        for glyph in font:
109            if doMask:
110                # open a glyph window
111                fl.EditGlyph(glyph.index)
112                # switch to the mask layer
113                fl.CallCommand(fl_cmd.ViewEditMask)
114                # if the mask is empty, skip this step
115                if not len(glyph):
116                    # switch back to the edit layer
117                    fl.CallCommand(fl_cmd.ViewEditMask)
118                    continue
119                # get the mask data
120                pen = InstructionPointPen()
121                glyph.drawPoints(pen)
122                # switch back to the edit layer
123                fl.CallCommand(fl_cmd.ViewEditMask)
124                # write the mask data to the glyph lib
125                instructions = pen.getInstructions()
126                if instructions:
127                    glyph.lib[MASK_LIB_KEY] = instructions
128            if doMark:
129                mark = glyph.mark
130                glyph.lib[MARK_LIB_KEY] = mark
131        # close all glyph windows. sometimes this actually works.
132        fl.CallCommand(fl_cmd.WindowCloseAllGlyphWindows)
133    # export the UFO
134    font.writeUFO(ufoPath, doProgress=doProgress)
135    # clear the data from the glyph lib
136    if doMask or doMark:
137        for glyph in font:
138            lib = glyph.lib
139            if lib.has_key(MASK_LIB_KEY):
140                del lib[MASK_LIB_KEY]
141            if lib.has_key(MARK_LIB_KEY):
142                del lib[MARK_LIB_KEY]
143            glyph.update()
144    #
145    font.update()
146
147def importUFOWithMetaData(font, ufoPath, doMask=True, doMark=True, doKerning=True, doProgress=False):
148    # make sure that the font is the top most font
149    fl.ifont = font.fontIndex
150    # import the UFO
151    font.readUFO(ufoPath, doProgress=doProgress)
152    # add the mask & mark data
153    if doMask or doMark:
154        for glyph in font:
155            lib = glyph.lib
156            # mask
157            if doMask:
158                if lib.has_key(MASK_LIB_KEY):
159                    # open a glyph window
160                    fl.EditGlyph(glyph.index)
161                    # switch to the mask layer
162                    fl.CallCommand(fl_cmd.ViewEditMask)
163                    # add the mask data
164                    instructions = lib[MASK_LIB_KEY]
165                    pen = glyph.getPointPen()
166                    instructionsDrawPoints(instructions, pen)
167                    # switch back to the edit layer
168                    fl.CallCommand(fl_cmd.ViewEditMask)
169                    # clear the mask data from the glyph lib
170                    del lib[MASK_LIB_KEY]
171            # mark
172            if doMark:
173                if lib.has_key(MARK_LIB_KEY):
174                    glyph.mark = lib[MARK_LIB_KEY]
175            glyph.update()
176        # close all glyph windows. sometimes this actually works.
177        fl.CallCommand(fl_cmd.WindowCloseAllGlyphWindows)
178    # clear the kerning if it is not to be imported
179    if not doKerning:
180        font.kerning.clear()
181    #
182    font.update()
183
184
185class UFOCentral(object):
186   
187    def __init__(self):
188        self.files = {}
189        self.mode = "export"
190        #
191        self.w = ModalDialog((375, 350), "UFO Central", okCallback=self.okCallback)
192        self.w.fileList = List((12, 12, 200, -60), [])
193        #
194        self.w.doExportCheckBox = CheckBox((220, 12, -12, 20), "Export UFOs", callback=self.doExportCallback, value=True)
195        self.w.doImportCheckBox = CheckBox((220, 35, -12, 20), "Import UFOs", callback=self.doImportCallback)
196        #
197        self.w.selectFileOrFolderButton = Button((220, 75, -12, 20), "Select file or folder", callback=self.selectFileOrFolderButtonCallback)
198        self.w.selectAllOpenButton = Button((220, 105, -12, 20), "All open fonts", callback=self.selectAllOpenCallback)
199        #
200        self.w.doMaskCheckBox = CheckBox((220, 145, -12, 20), "Process masks", value=True)
201        self.w.doMarkCheckBox = CheckBox((220, 170, -12, 20), "Process marks", value=True)
202        self.w.doKerningCheckBox = CheckBox((220, 195, -12, 20), "Import kerning")
203        self.w.doKerningCheckBox.enable(False)
204        #
205        self.w.open()
206
207    def _modeChange(self):
208        self.files = {}
209        self.w.fileList.set([])
210        if self.mode == "export":
211            self.w.selectAllOpenButton.enable(True)
212            self.w.doExportCheckBox.set(True)
213            self.w.doImportCheckBox.set(False)
214            self.w.doKerningCheckBox.enable(False)
215        else:
216            self.w.selectAllOpenButton.enable(False)
217            self.w.doExportCheckBox.set(False)
218            self.w.doImportCheckBox.set(True)
219            self.w.doKerningCheckBox.enable(True)
220
221    def _updateFileList(self):
222        fileNames = [os.path.basename(p) for p in self.files.keys()]
223        fileNames.sort()
224        self.w.fileList.set(fileNames)
225
226    def doExportCallback(self, sender):
227        if sender.get():
228            self.mode = "export"
229        else:
230            self.mode = "import"
231        self._modeChange()
232
233    def doImportCallback(self, sender):
234        if sender.get():
235            self.mode = "import"
236        else:
237            self.mode = "export"
238        self._modeChange()
239
240    def selectFileOrFolderButtonCallback(self, sender):
241        path = GetFolder()
242        if path is not None:
243            if os.path.isdir(path):
244                if os.path.splitext(path)[-1] == ".ufo" and self.mode == "import":
245                    self.files[path] = None
246                else:
247                    if self.mode == "export":
248                        pattern = "*.vfb"
249                    else:
250                        pattern = "*.ufo"
251                    for fileName in glob.glob(os.path.join(path, pattern)):
252                        self.files[fileName] = None
253            else:
254                ext = os.path.splitext(path)[-1]
255                if ext == ".vfb" and self.mode == "export":
256                    self.files[path] = None
257                elif ext == ".ufo" and self.mode == "import":
258                    self.files[path] = None
259            self._updateFileList()
260
261    def selectAllOpenCallback(self, sender):
262        all = [f for f in AllFonts() if f.path is not None]
263        for font in all:
264            self.files[font.path] = font
265        self._updateFileList()
266
267    def okCallback(self, sender):
268        if self.mode == "export":
269            doMask = self.w.doMaskCheckBox.get()
270            doMark = self.w.doMarkCheckBox.get()
271            for path, font in self.files.items():
272                if font is None:
273                    font = OpenFont(path)
274                exportUFOWithMetaData(font, doMask=doMask, doMark=doMark, doProgress=True)
275        else:
276            doMask = self.w.doMaskCheckBox.get()
277            doMark = self.w.doMarkCheckBox.get()
278            doKerning = self.w.doKerningCheckBox.get()
279            for path, null in self.files.items():
280                font = NewFont()
281                importUFOWithMetaData(font, ufoPath=path, doMask=doMask, doMark=doMark, doKerning=doKerning, doProgress=True)
282                vfbPath = os.path.splitext(path)[0] + ".vfb"
283                font.save(vfbPath)
284                font.close()
285
286
287if __name__ == "__main__":
288    UFOCentral()
Note: See TracBrowser for help on using the repository browser.