def testWrite(self): infoObject = self.makeInfoObject() writer = UFOWriter(self.dstDir, formatVersion=1) writer.writeInfo(infoObject) writtenData = self.readPlist() for attr, originalValue in list(fontInfoVersion1.items()): newValue = writtenData[attr] self.assertEqual(newValue, originalValue)
def save(self): print "Saving font..." """ A real hack here. If the font format was less than 3, we need to save it with the original format. I care specifically about RoboFont, which can still read only format 2. However, dfont.save() will save the processed layer only if the format is 3. If I ask to save(formatVersion=3) when the dfont format is 2, then the save function will first mark all the glyphs in all the layers as being 'dirty' and needing to be saved. and also causes the defcon font.py:save to delete the original font and write everything anew. In order to avoid this, I reach into the defcon code and save only the processed glyphs layer. """ from ufoLib import UFOWriter writer = UFOWriter(self.dFont.path, formatVersion=2) layers = self.dFont.layers layer = layers[kProcessedGlyphsLayerName] writer._formatVersion = 3 writer.layerContents[kProcessedGlyphsLayerName] = kProcessedGlyphsLayer glyphSet = writer.getGlyphSet(layerName=kProcessedGlyphsLayerName, defaultLayer=False) writer.writeLayerContents(layers.layerOrder) writer._formatVersion = 2 layer.save(glyphSet) layer.dirty = False if self.fontType == kUFOFontType: self.ufoFontHashData.hashMapChanged = True self.ufoFontHashData.close() # Write the hash data. elif self.fontType == kType1FontType: cmd = "tx -t1 \"%s\" \"%s\"" % (self.tempUFOPath, self.fontPath) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout log = p.read() elif self.fontType == kCFFFontType: cmd = "tx -cff +b -std \"%s\" \"%s\"" % (self.tempUFOPath, self.fontPath) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout log = p.read() elif self.fontType == kOpenTypeCFFFontType: tempCFFPath = self.tempUFOPath + ".cff" cmd = "tx -cff +b -std \"%s\" \"%s\"" % (self.tempUFOPath, tempCFFPath) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout log = p.read() cmd = "sfntedit -a \"CFF \"=\"%s\" \"%s\"" % (tempCFFPath, self.fontPath) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout log = p.read() if os.path.exists(tempCFFPath): os.remove(tempCFFPath) else: print "Font type is unknown: cannot save changes" if (self.tempUFOPath != None) and os.path.exists(self.tempUFOPath): shutil.rmtree(self.tempUFOPath)
def testFontStyleConversion(self): fontStyle1To2 = { 64: "regular", 1: "italic", 32: "bold", 33: "bold italic" } for old, new in list(fontStyle1To2.items()): infoObject = self.makeInfoObject() infoObject.styleMapStyleName = new writer = UFOWriter(self.dstDir, formatVersion=1) writer.writeInfo(infoObject) writtenData = self.readPlist() self.assertEqual(writtenData["fontStyle"], old)
def testWrite(self): writer = UFOWriter(self.dstDir, formatVersion=2) writer.setKerningGroupConversionRenameMaps(self.downConversionMapping) writer.writeKerning(self.kerning) writer.writeGroups(self.groups) # test groups path = os.path.join(self.dstDir, "groups.plist") writtenGroups = readPlist(path) self.assertEqual(writtenGroups, self.expectedWrittenGroups) # test kerning path = os.path.join(self.dstDir, "kerning.plist") writtenKerning = readPlist(path) self.assertEqual(writtenKerning, self.expectedWrittenKerning) self.tearDownUFO()
def write_ufo_font(path, hand): glyphs = generate_glyphs(hand) import shutil root = "%s.ufo" % path shutil.rmtree(root) writer = UFOWriter(root, fileCreator="net.team2xh.tenchi") writer.writeGroups({character: [character] for character in hand.glyphs}) writer.writeInfo(font_info(hand)) write_ufo_glyphs(glyphs, root) #writer._readLayerContents() return root
def test_pathlike(testufo): class PathLike(object): def __init__(self, s): self._path = s def __fspath__(self): return tostr(self._path, sys.getfilesystemencoding()) path = PathLike(testufo) with UFOReader(path) as reader: assert reader._path == path.__fspath__() with UFOWriter(path) as writer: assert writer._path == path.__fspath__()
def testWidthNameConversion(self): widthName1To2 = { "Ultra-condensed": 1, "Extra-condensed": 2, "Condensed": 3, "Semi-condensed": 4, "Medium (normal)": 5, "Semi-expanded": 6, "Expanded": 7, "Extra-expanded": 8, "Ultra-expanded": 9 } for old, new in list(widthName1To2.items()): infoObject = self.makeInfoObject() infoObject.openTypeOS2WidthClass = new writer = UFOWriter(self.dstDir, formatVersion=1) writer.writeInfo(infoObject) writtenData = self.readPlist() self.assertEqual(writtenData["widthName"], old)
def save(self, path): if path is None: path = self.path if os.path.abspath(self.path) != os.path.abspath(path): # If user has specified a path other than the source font path, # then copy the entire UFO font, and operate on the copy. log.info("Copying from source UFO font to output UFO font before " "processing...") if os.path.exists(path): shutil.rmtree(path) shutil.copytree(self.path, path) writer = UFOWriter(path, self._reader.formatVersion, validate=False) if self.hashMapChanged: self.writeHashMap(writer) self.hashMapChanged = False layer = PROCESSED_LAYER_NAME if self.writeToDefaultLayer: layer = None # Write glyphs. glyphset = writer.getGlyphSet(layer, defaultLayer=layer is None) for name, glyph in self.newGlyphMap.items(): filename = self.glyphMap[name] if not self.writeToDefaultLayer and \ name in self.processedLayerGlyphMap: filename = self.processedLayerGlyphMap[name] glyphset.contents[name] = filename glyphset.writeGlyph(name, glyph, glyph.drawPoints) glyphset.writeContents() # Write layer contents. layers = {DEFAULT_LAYER_NAME: DEFAULT_GLYPHS_DIRNAME} if self.processedLayerGlyphMap: layers[PROCESSED_LAYER_NAME] = PROCESSED_GLYPHS_DIRNAME writer.layerContents.update(layers) writer.writeLayerContents([DEFAULT_LAYER_NAME, PROCESSED_LAYER_NAME])
def save(self): print("Saving font...") """ A real hack here. If the font format was less than 3, we need to save it with the original format. I care specifically about RoboFont, which can still read only format 2. However, dfont.save() will save the processed layer only if the format is 3. If I ask to save(formatVersion=3) when the dfont format is 2, then the save function will first mark all the glyphs in all the layers as being 'dirty' and needing to be saved. and also causes the defcon font.py:save to delete the original font and write everything anew. In order to avoid this, I reach into the defcon code and save only the processed glyphs layer. I also set glyph_set.ufoFormatVersion so that the glif files will be set to format 1. """ from ufoLib import UFOWriter writer = UFOWriter(self.defcon_font.path, formatVersion=2) if self.save_to_default_layer: self.defcon_font.save() else: layers = self.defcon_font.layers layer = layers[PROCD_GLYPHS_LAYER_NAME] writer._formatVersion = 3 writer.layerContents[PROCD_GLYPHS_LAYER_NAME] = \ PROCD_GLYPHS_LAYER glyph_set = writer.getGlyphSet(layerName=PROCD_GLYPHS_LAYER_NAME, defaultLayer=False) writer.writeLayerContents(layers.layerOrder) glyph_set.ufoFormatVersion = 2 layer.save(glyph_set) layer.dirty = False if self.font_type == UFO_FONT_TYPE: # Write the hash data, if it has changed. self.ufo_font_hash_data.close() elif self.font_type == TYPE1_FONT_TYPE: cmd = "tx -t1 \"%s\" \"%s\"" % (self.temp_ufo_path, self.font_path) subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) elif self.font_type == CFF_FONT_TYPE: cmd = "tx -cff +b -std \"%s\" \"%s\"" % (self.temp_ufo_path, self.font_path) subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) elif self.font_type == OPENTYPE_CFF_FONT_TYPE: temp_cff_path = self.temp_ufo_path + ".cff" cmd = "tx -cff +b -std \"%s\" \"%s\"" % (self.temp_ufo_path, temp_cff_path) subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) cmd = "sfntedit -a \"CFF \"=\"%s\" \"%s\"" % (temp_cff_path, self.font_path) subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if os.path.exists(temp_cff_path): os.remove(temp_cff_path) else: print("Font type is unknown: cannot save changes") if (self.temp_ufo_path is not None) \ and os.path.exists(self.temp_ufo_path): shutil.rmtree(self.temp_ufo_path)
def test_init_writer(self): m = fs.memoryfs.MemoryFS() with UFOWriter(m) as writer: assert m.exists("metainfo.plist") assert writer._path == "<memfs>"
def test_path_attribute_deprecated(testufo): with UFOWriter(testufo) as writer: with pytest.warns(DeprecationWarning, match="The 'path' attribute"): writer.path
def test_write(self, testufoz): with UFOWriter(testufoz, structure="zip") as writer: writer.writeLib({"hello world": 123}) with UFOReader(testufoz) as reader: assert reader.readLib() == {"hello world": 123}
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)
existingFont = fontforge.open(existingFontPath) existingGlyphNames = set(existingFont.__iter__()) justInSource = sourceGlyphNames - existingGlyphNames # print 'glyphs in both:', sourceGlyphNames & existingGlyphNames # print 'glyphs only in source:', justInSource filtered = [name for name in justInSource if isValid(name)] overlapping = [name for name in (sourceGlyphNames & existingGlyphNames) if isValid(name)] print 'from the latin and valid:', sorted(filtered) print '------------------' print 'overlapping and valid', sorted(overlapping) writer = UFOWriter('sources/jomhuria-latin.ufo', formatVersion=2) newGlypsSet = writer.getGlyphSet() for glyphName in filtered + overlapping: # note how incestuous Glyph and GlyphSet interact. glyph = Glyph(glyphName, sourceGlyphSet) # this reads just the attributes sourceGlyphSet.readGlyph(glyphName, glyph) newGlypsSet.writeGlyph( glyphName , glyphObject=glyph , drawPointsFunc=glyph.drawPoints ) # after writing glyphs write the contents.plist