def run_ufolib_import_validation(self): """ ufoLib UFOReader.getLayerNames method validates the layercontents.plist file :return: (list) list of test failure Result objects """ res = Result(self.testpath) ss = StdStreamer(self.ufopath) if file_exists( self.testpath ) is False: # should only meet this condition if not a mandatory file (runner.py checks) res.test_failed = False ss.stream_result(res) return self.test_fail_list try: # read layercontents.plist with ufoLib - the ufoLib library performs type validations on values on read ufolib_reader = UFOReader(self.ufopath, validate=True) ufolib_reader.getLayerNames() res.test_failed = False ss.stream_result(res) except Exception as e: if self.testpath in self.mandatory_filepaths_list: # if part of mandatory file spec for UFO version, fail early res.test_failed = True res.exit_failure = True # fail early b/c it is mandatory part of spec else: res.test_failed = True # fail the test, but wait to report until all other tests complete res.test_long_stdstream_string = self.testpath + " failed ufoLib import test with error: " + str( e) self.test_fail_list.append(res) ss.stream_result(res) return self.test_fail_list
def run_ufolib_import_validation(self): """ ufoLib UFOReader.readMetaInfo method validates the UFO version number. This method adds validation for expected reverse URL name scheme in :return: (list) list of test failure Result objects """ res = Result(self.testpath) ss = StdStreamer(self.ufopath) if file_exists(self.testpath ) is False: # fail test, exit early if file is missing res.test_failed = True res.exit_failure = True res.test_long_stdstream_string = "metainfo.plist is not available on the path " + self.testpath ss.stream_result(res) try: ufolib_reader = UFOReader(self.ufopath, validate=True) ufolib_reader.readMetaInfo() res.test_failed = False ss.stream_result(res) except Exception as e: res.test_failed = True res.exit_failure = True res.test_long_stdstream_string = self.testpath + " failed ufoLib import test with error: " + str( e) self.test_fail_list.append(res) ss.stream_result(res) return self.test_fail_list
def reloadLayers(self, layerData): """ Reload the layers. This should not be called externally. """ reader = UFOReader(self.font.path) # handle the layers currentLayerOrder = self.layerOrder for layerName, l in list(layerData.get("layers", {}).items()): # new layer if layerName not in currentLayerOrder: glyphSet = reader.getGlyphSet(layerName) self.newLayer(layerName, glyphSet) # get the layer layer = self[layerName] # reload the layer info if l.get("info"): layer.color = None layer.lib.clear() layer._glyphSet.readLayerInfo(layer) self._stampLayerInfoDataState(layer) # reload the glyphs glyphNames = l.get("glyphNames", []) if glyphNames: layer.reloadGlyphs(glyphNames) # handle the order if layerData.get("order", False): newLayerOrder = reader.getLayerNames() for layerName in self.layerOrder: if layerName not in newLayerOrder: newLayerOrder.append(layerName) self.layerOrder = newLayerOrder # handle the default layer if layerData.get("default", False): newDefaultLayerName = reader.getDefaultLayerName() self.defaultLayer = self[newDefaultLayerName]
def __init__(self, path, log_only, allow_decimal_coords, write_to_default_layer): self._reader = UFOReader(path, validate=False) self.path = path self.glyphMap = {} self.processedLayerGlyphMap = {} self.newGlyphMap = {} self.glyphList = [] self._fontInfo = None self._glyphsets = {} # If True, we are running in report mode and not doing any changes, so # we skip the hash map and process all glyphs. self.log_only = log_only # Used to store the hash of glyph data of already processed glyphs. If # the stored hash matches the calculated one, we skip the glyph. self._hashmap = None self.fontDict = None self.hashMapChanged = False # If True, then write data to the default layer self.writeToDefaultLayer = write_to_default_layer # if True, do NOT round x,y values when processing. self.allowDecimalCoords = allow_decimal_coords self._load_glyphmap()
def testUFO2(self): self.makeUFO(formatVersion=2) reader = UFOReader(self.ufoPath) kerning = reader.readKerning() self.assertEqual(self.expectedKerning, kerning) groups = reader.readGroups() self.assertEqual(self.expectedGroups, groups) info = TestInfoObject() reader.readInfo(info)
def __getitem__(self, fileName): if self._data[fileName]["data"] is None: path = self.font.path reader = UFOReader(path) path = os.path.join("data", fileName) data = reader.readBytesFromPath(path) onDiskModTime = reader.getFileModificationTime(path) self._data[fileName] = _dataDict(data=data, onDisk=True, onDiskModTime=onDiskModTime) return self._data[fileName]["data"]
def __getitem__(self, fileName): if self._data[fileName]["data"] is None: path = self.font.path reader = UFOReader(path, validate=False) path = os.path.join("data", fileName) data = reader.readBytesFromPath(path) onDiskModTime = reader.getFileModificationTime(path) self._data[fileName] = _dataDict(data=data, onDisk=True, onDiskModTime=onDiskModTime) return self._data[fileName]["data"]
def __getitem__(self, fileName): d = self._data[fileName] if d["data"] is None: path = self.font.path reader = UFOReader(path) data = reader.readImage(fileName) d["data"] = data d["digest"] = _makeDigest(data) d["onDisk"] = True d["onDiskModTime"] = reader.getFileModificationTime(os.path.join("images", fileName)) return d["data"]
def testRead(self): originalData = dict(fontInfoVersion1) self._writeInfoToPlist(originalData) infoObject = TestInfoObject() reader = UFOReader(self.dstDir) reader.readInfo(infoObject) for attr in dir(infoObject): if attr not in fontInfoVersion2: continue originalValue = fontInfoVersion2[attr] readValue = getattr(infoObject, attr) self.assertEqual(originalValue, readValue)
def __getitem__(self, fileName): d = self._data[fileName] if d["data"] is None: path = self.font.path reader = UFOReader(path) data = reader.readImage(fileName) d["data"] = data d["digest"] = _makeDigest(data) d["onDisk"] = True d["onDiskModTime"] = reader.getFileModificationTime( os.path.join("images", fileName)) return d["data"]
def extractFontFromVFB(pathOrFile, destination, doGlyphs=True, doInfo=True, doKerning=True, doGroups=True, doFeatures=True, doLib=True, customFunctions=[]): ufoPath = tempfile.mkdtemp(suffix=".ufo") cmds = [_ufo2vfbLocation, "-64", pathOrFile, ufoPath] cmds = subprocess.list2cmdline(cmds) popen = subprocess.Popen(cmds, shell=True) popen.wait() try: # vfb2ufo writes ufo2, and has no update since 2015...so dont get to crazy here... source = UFOReader(ufoPath) if doInfo: source.readInfo(destination.info) if doKerning: kerning = source.readKerning() destination.kerning.update(kerning) if doGroups: groups = source.readGroups() destination.groups.update(groups) if doFeatures: features = source.readFeatures() destination.features.text = features if doLib: lib = source.readLib() destination.lib.update(lib) if doGlyphs: glyphSet = source.getGlyphSet() for glyphName in glyphSet.keys(): destination.newGlyph(glyphName) glyph = destination[glyphName] pointPen = glyph.getPointPen() glyphSet.readGlyph(glyphName=glyphName, glyphObject=glyph, pointPen=pointPen) for function in customFunctions: function(source, destination) finally: shutil.rmtree(ufoPath)
def test_testForExternalChanges_modify_on_disk_and_scan(self): path = makeTestFontCopy() font = Font(path) reader = UFOReader(path) # d = font.data["com.typesupply.defcon.test.file"] font.data["com.typesupply.defcon.test.file"] filePath = os.path.join(path, "data", "com.typesupply.defcon.test.file") f = open(filePath, "wb") f.write(b"blah") f.close() reader = UFOReader(path) self.assertEqual(font.data.testForExternalChanges(reader), (["com.typesupply.defcon.test.file"], [], [])) tearDownTestFontCopy()
def testFontStyleConversion(self): fontStyle1To2 = { 64: "regular", 1: "italic", 32: "bold", 33: "bold italic" } for old, new in list(fontStyle1To2.items()): info = dict(fontInfoVersion1) info["fontStyle"] = old self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) infoObject = TestInfoObject() reader.readInfo(infoObject) self.assertEqual(new, infoObject.styleMapStyleName)
def testFontStyleConversion(self): fontStyle1To2 = { 64 : "regular", 1 : "italic", 32 : "bold", 33 : "bold italic" } for old, new in list(fontStyle1To2.items()): info = dict(fontInfoVersion1) info["fontStyle"] = old self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) infoObject = TestInfoObject() reader.readInfo(infoObject) self.assertEqual(new, infoObject.styleMapStyleName)
def test_testExternalChanges_modify_in_memory_and_scan(self): path = makeTestFontCopy() font = Font(path) font.images["image 1.png"] = pngSignature + b"blah" reader = UFOReader(path) self.assertEqual(font.images.testForExternalChanges(reader), ([], [], [])) tearDownTestFontCopy()
def test_testExternalChanges_remove_on_disk_and_scan(self): path = makeTestFontCopy() font = Font(path) os.remove(os.path.join(path, "images", "image 1.png")) reader = UFOReader(path) self.assertEqual(font.images.testForExternalChanges(reader), ([], [], ["image 1.png"])) tearDownTestFontCopy()
def test_testForExternalChanges_add_in_memory_and_scan(self): path = makeTestFontCopy() font = Font(path) font.data["com.typesupply.defcon.test.file2"] = "blah" reader = UFOReader(path) self.assertEqual(font.data.testForExternalChanges(reader), ([], [], [])) tearDownTestFontCopy()
def test_testExternalChanges_remove_in_memory_and_scan(self): path = makeTestFontCopy() font = Font(path) del font.images["image 1.png"] reader = UFOReader(path) self.assertEqual(font.images.testForExternalChanges(reader), ([], [], [])) tearDownTestFontCopy()
def test_testForExternalChanges(self): path = getTestFontPath("TestExternalEditing.ufo") path = makeTestFontCopy(path) font = Font(path) reader = UFOReader(path) self.assertEqual(font.layers.testForExternalChanges(reader), {"deleted": [], "added": [], "modified": {}, "defaultLayer": False, "order": False}) tearDownTestFontCopy(font.path)
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()): info = dict(fontInfoVersion1) info["widthName"] = old self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) infoObject = TestInfoObject() reader.readInfo(infoObject) self.assertEqual(new, infoObject.openTypeOS2WidthClass)
def test_testForExternalChanges_add_on_disk_and_scan(self): import shutil path = makeTestFontCopy() font = Font(path) reader = UFOReader(path) source = os.path.join(path, "data", "com.typesupply.defcon.test.file") dest = os.path.join(path, "data", "com.typesupply.defcon.test.file2") shutil.copy(source, dest) self.assertEqual(font.data.testForExternalChanges(reader), ([], ["com.typesupply.defcon.test.file2"], [])) tearDownTestFontCopy()
def test_testExternalChanges_add_on_disk_and_scan(self): import shutil path = makeTestFontCopy() font = Font(path) source = os.path.join(path, "images", "image 1.png") dest = os.path.join(path, "images", "image 3.png") shutil.copy(source, dest) reader = UFOReader(path) self.assertEqual(font.images.testForExternalChanges(reader), ([], ["image 3.png"], [])) tearDownTestFontCopy()
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()): info = dict(fontInfoVersion1) info["widthName"] = old self._writeInfoToPlist(info) reader = UFOReader(self.dstDir) infoObject = TestInfoObject() reader.readInfo(infoObject) self.assertEqual(new, infoObject.openTypeOS2WidthClass)
def test_testForExternalChanges_remove_on_disk_and_scan(self): path = makeTestFontCopy() font = Font(path) reader = UFOReader(path) # image = font.data["com.typesupply.defcon.test.file"] font.data["com.typesupply.defcon.test.file"] os.remove(os.path.join(path, "data", "com.typesupply.defcon.test.file")) self.assertEqual(font.data.testForExternalChanges(reader), ([], [], ["com.typesupply.defcon.test.file"])) tearDownTestFontCopy()
def copyAnchors(font, latinUFO): ufo = UFOReader(latinUFO) glyphset = ufo.getGlyphSet() # mark types are: "mark" "base" "ligature" "basemark" "entry" "exit" className2markType = { 'MarkBelow': 'mark', 'TashkilBelow': 'mark', 'TashkilTashkilBelow': 'mark' } for name in glyphset.keys(): if name not in font: continue glyph = glyphset[name] # need to draw to receive the data of the glyph and that ControlBoundsPen is harmless glyph.draw(ControlBoundsPen(glyphset)) anchors = getattr(glyph, 'anchors', None) if not anchors: continue ffglyph = font[name] for anchor in anchors: ffglyph.addAnchorPoint(anchor['name'], className2markType[anchor['name']], anchor['x'], anchor['y'])
def _loadData(self, path): from ufoLib import UFOReader reader = UFOReader(path) fontLib = reader.readLib() # info reader.readInfo(self.info) # kerning self.kerning.update(reader.readKerning()) self.kerning.setChanged(False) # groups self.groups.update(reader.readGroups()) # features if reader.formatVersion == 1: # migrate features from the lib features = [] classes = fontLib.get("org.robofab.opentype.classes") if classes is not None: del fontLib["org.robofab.opentype.classes"] features.append(classes) splitFeatures = fontLib.get("org.robofab.opentype.features") if splitFeatures is not None: order = fontLib.get("org.robofab.opentype.featureorder") if order is None: order = splitFeatures.keys() order.sort() else: del fontLib["org.robofab.opentype.featureorder"] del fontLib["org.robofab.opentype.features"] for tag in order: oneFeature = splitFeatures.get(tag) if oneFeature is not None: features.append(oneFeature) features = "\n".join(features) else: features = reader.readFeatures() self.features.text = features # hint data self.psHints = PostScriptFontHintValues(self) if postScriptHintDataLibKey in fontLib: del fontLib[postScriptHintDataLibKey] # lib self.lib.update(fontLib) # glyphs self._glyphSet = reader.getGlyphSet() self._hasNotChanged(doGlyphs=False)
def save(self, writer, saveAs=False, progressBar=None): """ Save data. This method should not be called externally. Subclasses may override this method to implement custom saving behavior. """ if saveAs: font = self.font if font is not None and font.path is not None and os.path.exists( font.path): reader = UFOReader(font.path) readerDataDirectoryListing = reader.getDataDirectoryListing() for fileName, data in list(self._data.items()): path = os.path.join("data", fileName) if data["data"] is not None or fileName not in readerDataDirectoryListing: continue writer.copyFromReader(reader, path, path) for fileName in self._scheduledForDeletion: try: path = os.path.join("data", fileName) writer.removeFileForPath(path) except UFOLibError: # this will be raised if the file doesn't exist. # instead of trying to maintain a list of in UFO # vs. in memory, simply fail and move on when # something can't be deleted because it isn't # in the UFO. pass self._scheduledForDeletion.clear() reader = UFOReader(writer.path) for fileName, data in list(self._data.items()): if not data["dirty"]: continue path = os.path.join("data", fileName) writer.writeBytesToPath(path, data["data"]) data["dirty"] = False data["onDisk"] = True data["onDiskModTime"] = reader.getFileModificationTime( os.path.join("data", fileName)) self.dirty = False
def save(self, writer, removeUnreferencedImages=False, saveAs=False, progressBar=None): """ Save images. This method should not be called externally. Subclasses may override this method to implement custom saving behavior. """ if removeUnreferencedImages: self.disableNotifications() for fileName in self.unreferencedFileNames: del self[fileName] self.enableNotifications() if saveAs: font = self.font if font is not None and font.path is not None and os.path.exists(font.path): reader = UFOReader(font.path) readerImageNames = reader.getImageDirectoryListing() for fileName, data in self._data.items(): if data["data"] is not None or fileName not in readerImageNames: continue writer.copyImageFromReader(reader, fileName, fileName) for fileName in self._scheduledForDeletion: try: writer.removeImage(fileName) except UFOLibError: # this will be raised if the file doesn't exist. # instead of trying to maintain a list of in UFO # vs. in memory, simply fail and move on when # something can't be deleted because it isn't # in the UFO. pass self._scheduledForDeletion.clear() reader = UFOReader(writer.path) for fileName, data in self._data.items(): if not data["dirty"]: continue writer.writeImage(fileName, data["data"]) data["dirty"] = False data["onDisk"] = True data["onDiskModTime"] = reader.getFileModificationTime(os.path.join("images", fileName)) self.dirty = False
def testUFO2(self): self.makeUFO(formatVersion=2) reader = UFOReader(self.ufoPath, validate=True) kerning = reader.readKerning() self.assertEqual(self.expectedKerning, kerning) groups = reader.readGroups() self.assertEqual(self.expectedGroups, groups) info = TestInfoObject() reader.readInfo(info)
def test_testExternalChanges_modify_on_disk_and_scan(self): path = makeTestFontCopy() font = Font(path) font.images["image 1.png"] # image = font.images["image 1.png"] imagePath = os.path.join(path, "images", "image 1.png") f = open(imagePath, "rb") data = f.read() f.close() f = open(imagePath, "wb") f.write(data + b"blah") f.close() reader = UFOReader(path) self.assertEqual(font.images.testForExternalChanges(reader), (["image 1.png"], [], [])) tearDownTestFontCopy()
def test_testForExternalChanges_layerinfo(self): # layerinfo.plist path = getTestFontPath("TestExternalEditing.ufo") path = makeTestFontCopy(path) font = Font(path) reader = UFOReader(path) p = os.path.join(path, "glyphs", "layerinfo.plist") data = {"lib": {}} data["lib"]["testForExternalChanges.test"] = 1 with open(p, "wb") as f: dump(data, f) self.assertTrue( font.layers.testForExternalChanges(reader) ["modified"]["public.default"]["info"]) tearDownTestFontCopy(font.path)
def test_testForExternalChanges_add_a_layer(self): path = getTestFontPath("TestExternalEditing.ufo") path = makeTestFontCopy(path) font = Font(path) shutil.copytree(os.path.join(path, "glyphs"), os.path.join(path, "glyphs.test")) with open(os.path.join(path, "layercontents.plist"), "rb") as f: contents = load(f) contents.append(("test", "glyphs.test")) with open(os.path.join(path, "layercontents.plist"), "wb") as f: dump(contents, f) reader = UFOReader(path) self.assertEqual(font.layers.testForExternalChanges(reader)["added"], ["test"]) tearDownTestFontCopy(font.path)
def run_ufolib_import_validation(self): """ ufoLib UFOReader.readLib method validates the lib.plist file :return: (list) list of test failure Result objects """ res = Result(self.testpath) ss = StdStreamer(self.ufopath) if file_exists(self.testpath) is False: res.test_failed = False # not a mandatory file in UFO spec, test passes if missing ss.stream_result(res) return self.test_fail_list try: # read lib.plist with ufoLib - the ufoLib library performs type validations on values on read ufolib_reader = UFOReader(self.ufopath, validate=True) ufolib_reader.readLib() res.test_failed = False ss.stream_result(res) except Exception as e: res.test_failed = True res.test_long_stdstream_string = self.testpath + " failed ufoLib import test with error: " + str( e) ss.stream_result(res) self.test_fail_list.append(res) return self.test_fail_list
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 test_testForExternalChanges_change_default_layer(self): path = getTestFontPath("TestExternalEditing.ufo") path = makeTestFontCopy(path) shutil.copytree(os.path.join(path, "glyphs"), os.path.join(path, "glyphs.test")) contents = [("foo", "glyphs"), ("test", "glyphs.test")] with open(os.path.join(path, "layercontents.plist"), "wb") as f: dump(contents, f) font = Font(path) contents = [("test", "glyphs"), ("foo", "glyphs.test")] contents.reverse() with open(os.path.join(path, "layercontents.plist"), "wb") as f: dump(contents, f) reader = UFOReader(path) self.assertEqual(font.layers.testForExternalChanges(reader), {"deleted": [], "added": [], "modified": {}, "defaultLayer": True, "order": False}) tearDownTestFontCopy(font.path)
def _check_ufo_import_and_define_ufo_version(self): """ Tests UFO directory import with ufoLib UFOReader object and defines class property (ufo) with the ufoLib UFOReader object. This object is used for additional tests in this module. Failures added to the class property failures_list for final report :return: None """ ss = StdStreamer(self.ufopath) res = Result(self.ufopath) try: ufolib_reader = UFOReader(self.ufopath) self.ufoversion = ufolib_reader.formatVersion self.ufolib_reader = ufolib_reader res.test_failed = False ss.stream_result(res) except Exception as e: res.test_failed = True res.exit_failure = True res.test_long_stdstream_string = "ufoLib raised an exception with import of " + self.ufopath + os.linesep + str( e) self.failures_list.append(res) ss.stream_result(res)
def save(self, writer, removeUnreferencedImages=False, saveAs=False, progressBar=None): """ Save images. This method should not be called externally. Subclasses may override this method to implement custom saving behavior. """ if removeUnreferencedImages: self.disableNotifications() for fileName in self.unreferencedFileNames: del self[fileName] self.enableNotifications() if saveAs: font = self.font if font is not None and font.path is not None and os.path.exists( font.path): reader = UFOReader(font.path) readerImageNames = reader.getImageDirectoryListing() for fileName, data in self._data.items(): if data["data"] is not None or fileName not in readerImageNames: continue writer.copyImageFromReader(reader, fileName, fileName) for fileName in self._scheduledForDeletion: try: writer.removeImage(fileName) except UFOLibError: # this will be raised if the file doesn't exist. # instead of trying to maintain a list of in UFO # vs. in memory, simply fail and move on when # something can't be deleted because it isn't # in the UFO. pass self._scheduledForDeletion.clear() reader = UFOReader(writer.path) for fileName, data in self._data.items(): if not data["dirty"]: continue writer.writeImage(fileName, data["data"]) data["dirty"] = False data["onDisk"] = True data["onDiskModTime"] = reader.getFileModificationTime( os.path.join("images", fileName)) self.dirty = False