def main(args=None): from io import open options = parse_args(args) config = getConfig(options.config) svg_file = options.infile # Parse SVG to read the width, height attributes defined in it svgObj = parseSvg(svg_file) width = float(svgObj.attrib['width'].replace("px", " ")) height = float(svgObj.attrib['height'].replace("px", " ")) name = os.path.splitext(os.path.basename(svg_file))[0] ufo_font_path = config['font']['ufo'] # Get the font metadata from UFO reader = UFOReader(ufo_font_path) writer = UFOWriter(ufo_font_path) infoObject = InfoObject() reader.readInfo(infoObject) fontName = config['font']['name'] # Get the configuration for this svg try: svg_config = config['svgs'][name] except KeyError: print("\033[93mSkip: Configuration not found for svg : %r\033[0m" % name) return if 'unicode' in svg_config: unicodeVal = unicode_hex_list(svg_config['unicode']) else: unicodeVal = None glyphWidth = width + int(svg_config['left']) + int(svg_config['right']) if glyphWidth < 0: raise UFOLibError("Glyph %s has negative width." % name) contentsPlistPath = ufo_font_path + '/glyphs/contents.plist' try: with open(contentsPlistPath, "rb") as f: contentsPlist = readPlist(f) except: raise UFOLibError("The file %s could not be read." % contentsPlistPath) glyph_name = svg_config['glyph_name'] # Replace all capital letters with a following '_' to avoid file name clash in Windows glyph_file_name = re.sub(r'([A-Z]){1}', lambda pat: pat.group(1) + '_', glyph_name) + '.glif' if glyph_name in contentsPlist: existing_glyph = True else: existing_glyph = False # Calculate the transformation to do transform = transform_list(config['font']['transform']) base = 0 if 'base' in svg_config: base = int(svg_config['base']) transform[4] += int(svg_config['left']) # X offset = left bearing transform[5] += height + base # Y offset glif = svg2glif(svg_file, name=svg_config['glyph_name'], width=glyphWidth, height=getattr(infoObject, 'unitsPerEm'), unicodes=unicodeVal, transform=transform, version=config['font']['version']) if options.outfile is None: output_file = ufo_font_path + '/glyphs/' + glyph_file_name else: output_file = options.outfile with open(output_file, 'w', encoding='utf-8') as f: f.write(glif) print( "\033[94m[%s]\033[0m \033[92mConvert\033[0m %s -> %s \033[92m✔️\033[0m" % (fontName, name, output_file)) # If this is a new glyph, add it to the UFO/glyphs/contents.plist if not existing_glyph: contentsPlist[glyph_name] = glyph_file_name writePlistAtomically(contentsPlist, contentsPlistPath) print( "\033[94m[%s]\033[0m \033[92mAdd\033[0m %s -> %s \033[92m✔️\033[0m" % (fontName, glyph_name, glyph_file_name)) lib_obj = reader.readLib() lib_obj['public.glyphOrder'].append(glyph_name) writer.writeLib(lib_obj)
def save(self, destDir=None, doProgress=False, formatVersion=2): """Save the Font in UFO format.""" # XXX note that when doing "save as" by specifying the destDir argument # _all_ glyphs get loaded into memory. This could be optimized by either # copying those .glif files that have not been edited or (not sure how # well that would work) by simply clearing out self._objects after the # save. from ufoLib import UFOWriter from robofab.tools.fontlabFeatureSplitter import splitFeaturesForFontLab # if no destination is given, or if # the given destination is the current # path, this is not a save as operation if destDir is None or destDir == self._path: saveAs = False destDir = self._path else: saveAs = True # start a progress bar nonGlyphCount = 5 bar = None if doProgress: from robofab.interface.all.dialogs import ProgressBar bar = ProgressBar("Exporting UFO", nonGlyphCount + len(self._object.keys())) # write writer = UFOWriter(destDir, formatVersion=formatVersion) try: # make a shallow copy of the lib. stuff may be added to it. fontLib = dict(self.lib) # info if bar: bar.label("Saving info...") writer.writeInfo(self.info) if bar: bar.tick() # kerning if self.kerning.changed or saveAs: if bar: bar.label("Saving kerning...") writer.writeKerning(self.kerning.asDict()) if bar: bar.tick() # groups if bar: bar.label("Saving groups...") writer.writeGroups(self.groups) if bar: bar.tick() # features if bar: bar.label("Saving features...") features = self.features.text if features is None: features = "" if formatVersion == 2: writer.writeFeatures(features) elif formatVersion == 1: classes, features = splitFeaturesForFontLab(features) if classes: fontLib["org.robofab.opentype.classes"] = classes.strip() + "\n" if features: featureDict = {} for featureName, featureText in features: featureDict[featureName] = featureText.strip() + "\n" fontLib["org.robofab.opentype.features"] = featureDict fontLib["org.robofab.opentype.featureorder"] = [featureName for featureName, featureText in features] if bar: bar.tick() # lib if formatVersion == 1: fontLib[postScriptHintDataLibKey] = self.psHints.asDict() if bar: bar.label("Saving lib...") writer.writeLib(fontLib) if bar: bar.tick() # glyphs glyphNameToFileNameFunc = self.getGlyphNameToFileNameFunc() glyphSet = writer.getGlyphSet(glyphNameToFileNameFunc) if len(self._scheduledForDeletion) != 0: if bar: bar.label("Removing deleted glyphs...") for glyphName in self._scheduledForDeletion: if glyphSet.has_key(glyphName): glyphSet.deleteGlyph(glyphName) if bar: bar.tick() if bar: bar.label("Saving glyphs...") count = nonGlyphCount if saveAs: glyphNames = self.keys() else: glyphNames = self._object.keys() for glyphName in glyphNames: glyph = self[glyphName] glyph.psHints._saveToLib(glyph.lib) glyph._saveToGlyphSet(glyphSet, glyphName=glyphName, force=saveAs) if bar and not count % 10: bar.tick(count) count = count + 1 glyphSet.writeContents() self._glyphSet = glyphSet # only blindly stop if the user says to except KeyboardInterrupt: bar.close() bar = None # kill the progress bar if bar: bar.close() # reset internal stuff self._path = destDir self._scheduledForDeletion = [] self.setChanged(False)
def save(self, destDir=None, doProgress=False, formatVersion=2): """Save the Font in UFO format.""" # XXX note that when doing "save as" by specifying the destDir argument # _all_ glyphs get loaded into memory. This could be optimized by either # copying those .glif files that have not been edited or (not sure how # well that would work) by simply clearing out self._objects after the # save. from ufoLib import UFOWriter from robofab.tools.fontlabFeatureSplitter import splitFeaturesForFontLab # if no destination is given, or if # the given destination is the current # path, this is not a save as operation if destDir is None or destDir == self._path: saveAs = False destDir = self._path else: saveAs = True # start a progress bar nonGlyphCount = 5 bar = None if doProgress: from robofab.interface.all.dialogs import ProgressBar bar = ProgressBar("Exporting UFO", nonGlyphCount + len(self._object.keys())) # write writer = UFOWriter(destDir, formatVersion=formatVersion) try: # make a shallow copy of the lib. stuff may be added to it. fontLib = dict(self.lib) # info if bar: bar.label("Saving info...") writer.writeInfo(self.info) if bar: bar.tick() # kerning if self.kerning.changed or saveAs: if bar: bar.label("Saving kerning...") writer.writeKerning(self.kerning.asDict()) if bar: bar.tick() # groups if bar: bar.label("Saving groups...") writer.writeGroups(self.groups) if bar: bar.tick() # features if bar: bar.label("Saving features...") features = self.features.text if features is None: features = "" if formatVersion == 2: writer.writeFeatures(features) elif formatVersion == 1: classes, features = splitFeaturesForFontLab(features) if classes: fontLib["org.robofab.opentype.classes"] = classes.strip( ) + "\n" if features: featureDict = {} for featureName, featureText in features: featureDict[featureName] = featureText.strip() + "\n" fontLib["org.robofab.opentype.features"] = featureDict fontLib["org.robofab.opentype.featureorder"] = [ featureName for featureName, featureText in features ] if bar: bar.tick() # lib if formatVersion == 1: fontLib[postScriptHintDataLibKey] = self.psHints.asDict() if bar: bar.label("Saving lib...") writer.writeLib(fontLib) if bar: bar.tick() # glyphs glyphNameToFileNameFunc = self.getGlyphNameToFileNameFunc() glyphSet = writer.getGlyphSet(glyphNameToFileNameFunc) if len(self._scheduledForDeletion) != 0: if bar: bar.label("Removing deleted glyphs...") for glyphName in self._scheduledForDeletion: if glyphSet.has_key(glyphName): glyphSet.deleteGlyph(glyphName) if bar: bar.tick() if bar: bar.label("Saving glyphs...") count = nonGlyphCount if saveAs: glyphNames = self.keys() else: glyphNames = self._object.keys() for glyphName in glyphNames: glyph = self[glyphName] glyph.psHints._saveToLib(glyph.lib) glyph._saveToGlyphSet(glyphSet, glyphName=glyphName, force=saveAs) if bar and not count % 10: bar.tick(count) count = count + 1 glyphSet.writeContents() self._glyphSet = glyphSet # only blindly stop if the user says to except KeyboardInterrupt: bar.close() bar = None # kill the progress bar if bar: bar.close() # reset internal stuff self._path = destDir self._scheduledForDeletion = [] self.setChanged(False)