Add in more valiator switches

This commit is contained in:
Ben Kiel 2018-06-11 23:13:00 -05:00
parent 4c75212ee6
commit 83303d56a7
4 changed files with 169 additions and 142 deletions

View File

@ -151,7 +151,7 @@ class UFOReader(object):
``validate`` will validate the data. ``validate`` will validate the data.
""" """
if self._upConvertedKerningData: if validate and self._upConvertedKerningData:
testKerning = self._readKerning() testKerning = self._readKerning()
if testKerning != self._upConvertedKerningData["originalKerning"]: if testKerning != self._upConvertedKerningData["originalKerning"]:
raise UFOLibError("The data in kerning.plist has been modified since it was converted to UFO 3 format.") raise UFOLibError("The data in kerning.plist has been modified since it was converted to UFO 3 format.")
@ -326,9 +326,9 @@ class UFOReader(object):
# fontinfo.plist # fontinfo.plist
def _readInfo(self): def _readInfo(self, validate):
data = self._getPlist(FONTINFO_FILENAME, {}) data = self._getPlist(FONTINFO_FILENAME, {})
if not isinstance(data, dict): if validate and not isinstance(data, dict):
raise UFOLibError("fontinfo.plist is not properly formatted.") raise UFOLibError("fontinfo.plist is not properly formatted.")
return data return data
@ -344,7 +344,7 @@ class UFOReader(object):
""" """
if validate is None: if validate is None:
validate = self._validate validate = self._validate
infoDict = self._readInfo() infoDict = self._readInfo(validate)
infoDataToSet = {} infoDataToSet = {}
# version 1 # version 1
if self._formatVersion == 1: if self._formatVersion == 1:
@ -493,6 +493,7 @@ class UFOReader(object):
if layerDirectory == DEFAULT_GLYPHS_DIRNAME: if layerDirectory == DEFAULT_GLYPHS_DIRNAME:
return layerName return layerName
# this will already have been raised during __init__ # this will already have been raised during __init__
if validate:
raise UFOLibError("The default layer is not defined in layercontents.plist.") raise UFOLibError("The default layer is not defined in layercontents.plist.")
def getGlyphSet(self, layerName=None, validateRead=None, validateWrite=None): def getGlyphSet(self, layerName=None, validateRead=None, validateWrite=None):
@ -615,10 +616,10 @@ class UFOReader(object):
""" """
if validate is None: if validate is None:
validate = self._validate validate = self._validate
if self._formatVersion < 3: if validate and self._formatVersion < 3:
raise UFOLibError("Reading images is not allowed in UFO %d." % self._formatVersion) raise UFOLibError("Reading images is not allowed in UFO %d." % self._formatVersion)
data = self.readBytesFromPath(os.path.join(IMAGES_DIRNAME, fileName)) data = self.readBytesFromPath(os.path.join(IMAGES_DIRNAME, fileName))
if data is None: if validate and data is None:
raise UFOLibError("No image file named %s." % fileName) raise UFOLibError("No image file named %s." % fileName)
if validate: if validate:
valid, error = pngValidator(data=data) valid, error = pngValidator(data=data)
@ -661,10 +662,10 @@ class UFOWriter(object):
previousFormatVersion = int(previousFormatVersion) previousFormatVersion = int(previousFormatVersion)
except: except:
raise UFOLibError("The existing metainfo.plist is not properly formatted.") raise UFOLibError("The existing metainfo.plist is not properly formatted.")
if previousFormatVersion not in supportedUFOFormatVersions: if validate and previousFormatVersion not in supportedUFOFormatVersions:
raise UFOLibError("Unsupported UFO format (%d)." % formatVersion) raise UFOLibError("Unsupported UFO format (%d)." % formatVersion)
# catch down conversion # catch down conversion
if previousFormatVersion is not None and previousFormatVersion > formatVersion: if validate and previousFormatVersion is not None and previousFormatVersion > formatVersion:
raise UFOLibError("The UFO located at this path is a higher version (%d) than the version (%d) that is trying to be written. This is not supported." % (previousFormatVersion, formatVersion)) raise UFOLibError("The UFO located at this path is a higher version (%d) than the version (%d) that is trying to be written. This is not supported." % (previousFormatVersion, formatVersion))
# handle the layer contents # handle the layer contents
self.layerContents = {} self.layerContents = {}
@ -1056,11 +1057,14 @@ class UFOWriter(object):
# features.fea # features.fea
def writeFeatures(self, features): def writeFeatures(self, features, validate=None):
""" """
Write features.fea. This method requires a Write features.fea. This method requires a
features string as an argument. features string as an argument.
""" """
if validate is None:
validate = self._validate
if validate:
if self._formatVersion == 1: if self._formatVersion == 1:
raise UFOLibError("features.fea is not allowed in UFO Format Version 1.") raise UFOLibError("features.fea is not allowed in UFO Format Version 1.")
if not isinstance(features, basestring): if not isinstance(features, basestring):
@ -1090,11 +1094,13 @@ class UFOWriter(object):
contents[layerName] = directoryName contents[layerName] = directoryName
self.layerContents = contents self.layerContents = contents
def writeLayerContents(self, layerOrder=None): def writeLayerContents(self, layerOrder=None, validate=None):
""" """
Write the layercontents.plist file. This method *must* be called Write the layercontents.plist file. This method *must* be called
after all glyph sets have been written. after all glyph sets have been written.
""" """
if validate is None:
validate = self._validate
if self.formatVersion < 3: if self.formatVersion < 3:
return return
if layerOrder is not None: if layerOrder is not None:
@ -1106,7 +1112,7 @@ class UFOWriter(object):
layerOrder = newOrder layerOrder = newOrder
else: else:
layerOrder = list(self.layerContents.keys()) layerOrder = list(self.layerContents.keys())
if set(layerOrder) != set(self.layerContents.keys()): if validate and set(layerOrder) != set(self.layerContents.keys()):
raise UFOLibError("The layer order contents does not match the glyph sets that have been created.") raise UFOLibError("The layer order contents does not match the glyph sets that have been created.")
layerContents = [(layerName, self.layerContents[layerName]) for layerName in layerOrder] layerContents = [(layerName, self.layerContents[layerName]) for layerName in layerOrder]
self._writePlist(LAYERCONTENTS_FILENAME, layerContents) self._writePlist(LAYERCONTENTS_FILENAME, layerContents)
@ -1143,7 +1149,7 @@ class UFOWriter(object):
if validateWrite is None: if validateWrite is None:
validateWrite = self._validate validateWrite = self._validate
# only default can be written in < 3 # only default can be written in < 3
if self._formatVersion < 3 and (not defaultLayer or layerName is not None): if validateWrite and self._formatVersion < 3 and (not defaultLayer or layerName is not None):
raise UFOLibError("Only the default layer can be writen in UFO %d." % self.formatVersion) raise UFOLibError("Only the default layer can be writen in UFO %d." % self.formatVersion)
# locate a layer name when None has been given # locate a layer name when None has been given
if layerName is None and defaultLayer: if layerName is None and defaultLayer:
@ -1152,7 +1158,7 @@ class UFOWriter(object):
layerName = existingLayerName layerName = existingLayerName
if layerName is None: if layerName is None:
layerName = DEFAULT_LAYER_NAME layerName = DEFAULT_LAYER_NAME
elif layerName is None and not defaultLayer: elif validateWrite and layerName is None and not defaultLayer:
raise UFOLibError("A layer name must be provided for non-default layers.") raise UFOLibError("A layer name must be provided for non-default layers.")
# move along to format specific writing # move along to format specific writing
if self.formatVersion == 1: if self.formatVersion == 1:
@ -1175,6 +1181,7 @@ class UFOWriter(object):
# matches the default being written. also make sure that this layer # matches the default being written. also make sure that this layer
# name is not already linked to a non-default layer. # name is not already linked to a non-default layer.
if defaultLayer: if defaultLayer:
if validateRead:
for existingLayerName, directory in list(self.layerContents.items()): for existingLayerName, directory in list(self.layerContents.items()):
if directory == DEFAULT_GLYPHS_DIRNAME: if directory == DEFAULT_GLYPHS_DIRNAME:
if existingLayerName != layerName: if existingLayerName != layerName:
@ -1282,22 +1289,28 @@ class UFOWriter(object):
path = os.path.join(IMAGES_DIRNAME, fileName) path = os.path.join(IMAGES_DIRNAME, fileName)
self.writeBytesToPath(path, data) self.writeBytesToPath(path, data)
def removeImage(self, fileName): def removeImage(self, fileName, validate=None):
""" """
Remove the file named fileName from the Remove the file named fileName from the
images directory. images directory.
""" """
if validate is None:
validate = self._validate
if validate:
if self._formatVersion < 3: if self._formatVersion < 3:
raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion) raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion)
path = os.path.join(IMAGES_DIRNAME, fileName) path = os.path.join(IMAGES_DIRNAME, fileName)
self.removeFileForPath(path) self.removeFileForPath(path)
def copyImageFromReader(self, reader, sourceFileName, destFileName): def copyImageFromReader(self, reader, sourceFileName, destFileName, validate=None):
""" """
Copy the sourceFileName in the provided UFOReader to destFileName Copy the sourceFileName in the provided UFOReader to destFileName
in this writer. This uses the most memory efficient method possible in this writer. This uses the most memory efficient method possible
for copying the data possible. for copying the data possible.
""" """
if validate is None:
validate = self._validate
if validate:
if self._formatVersion < 3: if self._formatVersion < 3:
raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion) raise UFOLibError("Images are not allowed in UFO %d." % self._formatVersion)
sourcePath = os.path.join("images", sourceFileName) sourcePath = os.path.join("images", sourceFileName)

View File

@ -202,9 +202,9 @@ class GlyphSet(object):
infoDict = self._readPlist(path) infoDict = self._readPlist(path)
except MissingPlistError: except MissingPlistError:
return return
if validateRead:
if not isinstance(infoDict, dict): if not isinstance(infoDict, dict):
raise GlifLibError("layerinfo.plist is not properly formatted.") raise GlifLibError("layerinfo.plist is not properly formatted.")
if validateRead:
infoDict = validateLayerInfoVersion3Data(infoDict) infoDict = validateLayerInfoVersion3Data(infoDict)
# populate the object # populate the object
for attr, value in infoDict.items(): for attr, value in infoDict.items():
@ -220,7 +220,7 @@ class GlyphSet(object):
""" """
if validateWrite is None: if validateWrite is None:
validateWrite = self._validateWrite validateWrite = self._validateWrite
if self.ufoFormatVersion < 3: if validateWrite and self.ufoFormatVersion < 3:
raise GlifLibError("layerinfo.plist is not allowed in UFO %d." % self.ufoFormatVersion) raise GlifLibError("layerinfo.plist is not allowed in UFO %d." % self.ufoFormatVersion)
# gather data # gather data
infoData = {} infoData = {}
@ -375,7 +375,7 @@ class GlyphSet(object):
formatVersion = 2 formatVersion = 2
else: else:
formatVersion = 1 formatVersion = 1
else: elif validate:
if formatVersion not in supportedGLIFFormatVersions: if formatVersion not in supportedGLIFFormatVersions:
raise GlifLibError("Unsupported GLIF format version: %s" % formatVersion) raise GlifLibError("Unsupported GLIF format version: %s" % formatVersion)
if formatVersion == 2 and self.ufoFormatVersion < 3: if formatVersion == 2 and self.ufoFormatVersion < 3:
@ -590,20 +590,20 @@ def writeGlyphToString(glyphName, glyphObject=None, drawPointsFunc=None, writer=
aFile = None aFile = None
identifiers = set() identifiers = set()
# start # start
if not isinstance(glyphName, basestring): if validate and not isinstance(glyphName, basestring):
raise GlifLibError("The glyph name is not properly formatted.") raise GlifLibError("The glyph name is not properly formatted.")
if len(glyphName) == 0: if validate and len(glyphName) == 0:
raise GlifLibError("The glyph name is empty.") raise GlifLibError("The glyph name is empty.")
writer.begintag("glyph", [("name", glyphName), ("format", formatVersion)]) writer.begintag("glyph", [("name", glyphName), ("format", formatVersion)])
writer.newline() writer.newline()
# advance # advance
_writeAdvance(glyphObject, writer) _writeAdvance(glyphObject, writer, validate)
# unicodes # unicodes
if getattr(glyphObject, "unicodes", None): if getattr(glyphObject, "unicodes", None):
_writeUnicodes(glyphObject, writer) _writeUnicodes(glyphObject, writer, validate)
# note # note
if getattr(glyphObject, "note", None): if getattr(glyphObject, "note", None):
_writeNote(glyphObject, writer) _writeNote(glyphObject, writer, validate)
# image # image
if formatVersion >= 2 and getattr(glyphObject, "image", None): if formatVersion >= 2 and getattr(glyphObject, "image", None):
_writeImage(glyphObject, writer, validate) _writeImage(glyphObject, writer, validate)
@ -636,16 +636,16 @@ def writeGlyphToString(glyphName, glyphObject=None, drawPointsFunc=None, writer=
else: else:
return None return None
def _writeAdvance(glyphObject, writer): def _writeAdvance(glyphObject, writer, validate):
width = getattr(glyphObject, "width", None) width = getattr(glyphObject, "width", None)
if width is not None: if width is not None:
if not isinstance(width, (int, float)): if validate and not isinstance(width, (int, float)):
raise GlifLibError("width attribute must be int or float") raise GlifLibError("width attribute must be int or float")
if width == 0: if width == 0:
width = None width = None
height = getattr(glyphObject, "height", None) height = getattr(glyphObject, "height", None)
if height is not None: if height is not None:
if not isinstance(height, (int, float)): if validate and not isinstance(height, (int, float)):
raise GlifLibError("height attribute must be int or float") raise GlifLibError("height attribute must be int or float")
if height == 0: if height == 0:
height = None height = None
@ -659,13 +659,13 @@ def _writeAdvance(glyphObject, writer):
writer.simpletag("advance", height=repr(height)) writer.simpletag("advance", height=repr(height))
writer.newline() writer.newline()
def _writeUnicodes(glyphObject, writer): def _writeUnicodes(glyphObject, writer, validate):
unicodes = getattr(glyphObject, "unicodes", None) unicodes = getattr(glyphObject, "unicodes", None)
if isinstance(unicodes, int): if validate and isinstance(unicodes, int):
unicodes = [unicodes] unicodes = [unicodes]
seen = set() seen = set()
for code in unicodes: for code in unicodes:
if not isinstance(code, int): if validate and not isinstance(code, int):
raise GlifLibError("unicode values must be int") raise GlifLibError("unicode values must be int")
if code in seen: if code in seen:
continue continue
@ -674,9 +674,9 @@ def _writeUnicodes(glyphObject, writer):
writer.simpletag("unicode", hex=hexCode) writer.simpletag("unicode", hex=hexCode)
writer.newline() writer.newline()
def _writeNote(glyphObject, writer): def _writeNote(glyphObject, writer, validate):
note = getattr(glyphObject, "note", None) note = getattr(glyphObject, "note", None)
if not isinstance(note, basestring): if validate and not isinstance(note, basestring):
raise GlifLibError("note attribute must be str or unicode") raise GlifLibError("note attribute must be str or unicode")
note = note.encode("utf-8") note = note.encode("utf-8")
writer.begintag("note") writer.begintag("note")
@ -727,7 +727,7 @@ def _writeGuidelines(glyphObject, writer, identifiers, validate):
attrs.append(("color", color)) attrs.append(("color", color))
identifier = guideline.get("identifier") identifier = guideline.get("identifier")
if identifier is not None: if identifier is not None:
if identifier in identifiers: if validate and identifier in identifiers:
raise GlifLibError("identifier used more than once: %s" % identifier) raise GlifLibError("identifier used more than once: %s" % identifier)
attrs.append(("identifier", identifier)) attrs.append(("identifier", identifier))
identifiers.add(identifier) identifiers.add(identifier)
@ -768,7 +768,7 @@ def _writeAnchors(glyphObject, writer, identifiers, validate):
attrs.append(("color", color)) attrs.append(("color", color))
identifier = anchor.get("identifier") identifier = anchor.get("identifier")
if identifier is not None: if identifier is not None:
if identifier in identifiers: if validate and identifier in identifiers:
raise GlifLibError("identifier used more than once: %s" % identifier) raise GlifLibError("identifier used more than once: %s" % identifier)
attrs.append(("identifier", identifier)) attrs.append(("identifier", identifier))
identifiers.add(identifier) identifiers.add(identifier)
@ -868,14 +868,14 @@ def _glifTreeFromString(aString):
def _readGlyphFromTree(tree, glyphObject=None, pointPen=None, formatVersions=(1, 2), validate=False): def _readGlyphFromTree(tree, glyphObject=None, pointPen=None, formatVersions=(1, 2), validate=False):
# check the format version # check the format version
formatVersion = tree.get("format") formatVersion = tree.get("format")
if formatVersion is None: if validate and formatVersion is None:
raise GlifLibError("Unspecified format version in GLIF.") raise GlifLibError("Unspecified format version in GLIF.")
try: try:
v = int(formatVersion) v = int(formatVersion)
formatVersion = v formatVersion = v
except ValueError: except ValueError:
pass pass
if formatVersion not in formatVersions: if validate and formatVersion not in formatVersions:
raise GlifLibError("Forbidden GLIF format version: %s" % formatVersion) raise GlifLibError("Forbidden GLIF format version: %s" % formatVersion)
if formatVersion == 1: if formatVersion == 1:
_readGlyphFromTreeFormat1(tree=tree, glyphObject=glyphObject, pointPen=pointPen, validate=validate) _readGlyphFromTreeFormat1(tree=tree, glyphObject=glyphObject, pointPen=pointPen, validate=validate)
@ -887,12 +887,13 @@ def _readGlyphFromTree(tree, glyphObject=None, pointPen=None, formatVersions=(1,
def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=None): def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=None):
# get the name # get the name
_readName(glyphObject, tree) _readName(glyphObject, tree, validate)
# populate the sub elements # populate the sub elements
unicodes = [] unicodes = []
haveSeenAdvance = haveSeenOutline = haveSeenLib = haveSeenNote = False haveSeenAdvance = haveSeenOutline = haveSeenLib = haveSeenNote = False
for element in tree: for element in tree:
if element.tag == "outline": if element.tag == "outline":
if validate:
if haveSeenOutline: if haveSeenOutline:
raise GlifLibError("The outline element occurs more than once.") raise GlifLibError("The outline element occurs more than once.")
if element.attrib: if element.attrib:
@ -904,7 +905,7 @@ def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=No
elif glyphObject is None: elif glyphObject is None:
continue continue
elif element.tag == "advance": elif element.tag == "advance":
if haveSeenAdvance: if validate and haveSeenAdvance:
raise GlifLibError("The advance element occurs more than once.") raise GlifLibError("The advance element occurs more than once.")
haveSeenAdvance = True haveSeenAdvance = True
_readAdvance(glyphObject, element) _readAdvance(glyphObject, element)
@ -917,12 +918,12 @@ def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=No
except ValueError: except ValueError:
raise GlifLibError("Illegal value for hex attribute of unicode element.") raise GlifLibError("Illegal value for hex attribute of unicode element.")
elif element.tag == "note": elif element.tag == "note":
if haveSeenNote: if validate and haveSeenNote:
raise GlifLibError("The note element occurs more than once.") raise GlifLibError("The note element occurs more than once.")
haveSeenNote = True haveSeenNote = True
_readNote(glyphObject, element) _readNote(glyphObject, element)
elif element.tag == "lib": elif element.tag == "lib":
if haveSeenLib: if validate and haveSeenLib:
raise GlifLibError("The lib element occurs more than once.") raise GlifLibError("The lib element occurs more than once.")
haveSeenLib = True haveSeenLib = True
_readLib(glyphObject, element, validate) _readLib(glyphObject, element, validate)
@ -934,7 +935,7 @@ def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=No
def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=None): def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=None):
# get the name # get the name
_readName(glyphObject, tree) _readName(glyphObject, tree, validate)
# populate the sub elements # populate the sub elements
unicodes = [] unicodes = []
guidelines = [] guidelines = []
@ -943,6 +944,7 @@ def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=No
identifiers = set() identifiers = set()
for element in tree: for element in tree:
if element.tag == "outline": if element.tag == "outline":
if validate:
if haveSeenOutline: if haveSeenOutline:
raise GlifLibError("The outline element occurs more than once.") raise GlifLibError("The outline element occurs more than once.")
if element.attrib: if element.attrib:
@ -955,7 +957,7 @@ def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=No
elif glyphObject is None: elif glyphObject is None:
continue continue
elif element.tag == "advance": elif element.tag == "advance":
if haveSeenAdvance: if validate and haveSeenAdvance:
raise GlifLibError("The advance element occurs more than once.") raise GlifLibError("The advance element occurs more than once.")
haveSeenAdvance = True haveSeenAdvance = True
_readAdvance(glyphObject, element) _readAdvance(glyphObject, element)
@ -968,20 +970,21 @@ def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=No
except ValueError: except ValueError:
raise GlifLibError("Illegal value for hex attribute of unicode element.") raise GlifLibError("Illegal value for hex attribute of unicode element.")
elif element.tag == "guideline": elif element.tag == "guideline":
if len(element): if validate and len(element):
raise GlifLibError("Unknown children in guideline element.") raise GlifLibError("Unknown children in guideline element.")
for attr in ("x", "y", "angle"): for attr in ("x", "y", "angle"):
if attr in element.attrib: if attr in element.attrib:
element.attrib[attr] = _number(element.attrib[attr]) element.attrib[attr] = _number(element.attrib[attr])
guidelines.append(element.attrib) guidelines.append(element.attrib)
elif element.tag == "anchor": elif element.tag == "anchor":
if len(element): if validate and len(element):
raise GlifLibError("Unknown children in anchor element.") raise GlifLibError("Unknown children in anchor element.")
for attr in ("x", "y"): for attr in ("x", "y"):
if attr in element.attrib: if attr in element.attrib:
element.attrib[attr] = _number(element.attrib[attr]) element.attrib[attr] = _number(element.attrib[attr])
anchors.append(element.attrib) anchors.append(element.attrib)
elif element.tag == "image": elif element.tag == "image":
if validate:
if haveSeenImage: if haveSeenImage:
raise GlifLibError("The image element occurs more than once.") raise GlifLibError("The image element occurs more than once.")
if len(element): if len(element):
@ -989,12 +992,12 @@ def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=No
haveSeenImage = True haveSeenImage = True
_readImage(glyphObject, element, validate) _readImage(glyphObject, element, validate)
elif element.tag == "note": elif element.tag == "note":
if haveSeenNote: if validate and haveSeenNote:
raise GlifLibError("The note element occurs more than once.") raise GlifLibError("The note element occurs more than once.")
haveSeenNote = True haveSeenNote = True
_readNote(glyphObject, element) _readNote(glyphObject, element)
elif element.tag == "lib": elif element.tag == "lib":
if haveSeenLib: if validate and haveSeenLib:
raise GlifLibError("The lib element occurs more than once.") raise GlifLibError("The lib element occurs more than once.")
haveSeenLib = True haveSeenLib = True
_readLib(glyphObject, element, validate) _readLib(glyphObject, element, validate)
@ -1005,18 +1008,18 @@ def _readGlyphFromTreeFormat2(tree, glyphObject=None, pointPen=None, validate=No
_relaxedSetattr(glyphObject, "unicodes", unicodes) _relaxedSetattr(glyphObject, "unicodes", unicodes)
# set the collected guidelines # set the collected guidelines
if guidelines: if guidelines:
if not guidelinesValidator(guidelines, identifiers): if validate and not guidelinesValidator(guidelines, identifiers):
raise GlifLibError("The guidelines are improperly formatted.") raise GlifLibError("The guidelines are improperly formatted.")
_relaxedSetattr(glyphObject, "guidelines", guidelines) _relaxedSetattr(glyphObject, "guidelines", guidelines)
# set the collected anchors # set the collected anchors
if anchors: if anchors:
if not anchorsValidator(anchors, identifiers): if validate and not anchorsValidator(anchors, identifiers):
raise GlifLibError("The anchors are improperly formatted.") raise GlifLibError("The anchors are improperly formatted.")
_relaxedSetattr(glyphObject, "anchors", anchors) _relaxedSetattr(glyphObject, "anchors", anchors)
def _readName(glyphObject, root): def _readName(glyphObject, root, validate):
glyphName = root.get("name") glyphName = root.get("name")
if not glyphName: if validate and not glyphName:
raise GlifLibError("Empty glyph name in GLIF.") raise GlifLibError("Empty glyph name in GLIF.")
if glyphName and glyphObject is not None: if glyphName and glyphObject is not None:
_relaxedSetattr(glyphObject, "name", glyphName) _relaxedSetattr(glyphObject, "name", glyphName)
@ -1072,7 +1075,7 @@ def buildOutlineFormat1(glyphObject, pen, outline, validate):
if len(element) == 1: if len(element) == 1:
point = element[0] point = element[0]
if point.tag == "point": if point.tag == "point":
anchor = _buildAnchorFormat1(point) anchor = _buildAnchorFormat1(point, validate)
if anchor is not None: if anchor is not None:
anchors.append(anchor) anchors.append(anchor)
continue continue
@ -1080,7 +1083,7 @@ def buildOutlineFormat1(glyphObject, pen, outline, validate):
_buildOutlineContourFormat1(pen, element, validate) _buildOutlineContourFormat1(pen, element, validate)
elif element.tag == "component": elif element.tag == "component":
if pen is not None: if pen is not None:
_buildOutlineComponentFormat1(pen, element) _buildOutlineComponentFormat1(pen, element, validate)
else: else:
raise GlifLibError("Unknown element in outline element: %s" % element) raise GlifLibError("Unknown element in outline element: %s" % element)
if glyphObject is not None and anchors: if glyphObject is not None and anchors:
@ -1088,7 +1091,7 @@ def buildOutlineFormat1(glyphObject, pen, outline, validate):
raise GlifLibError("GLIF 1 anchors are not properly formatted.") raise GlifLibError("GLIF 1 anchors are not properly formatted.")
_relaxedSetattr(glyphObject, "anchors", anchors) _relaxedSetattr(glyphObject, "anchors", anchors)
def _buildAnchorFormat1(point): def _buildAnchorFormat1(point, validate):
if point.get("type") != "move": if point.get("type") != "move":
return None return None
name = point.get("name") name = point.get("name")
@ -1096,9 +1099,9 @@ def _buildAnchorFormat1(point):
return None return None
x = point.get("x") x = point.get("x")
y = point.get("y") y = point.get("y")
if x is None: if validate and x is None:
raise GlifLibError("Required x attribute is missing in point element.") raise GlifLibError("Required x attribute is missing in point element.")
if y is None: if validate and y is None:
raise GlifLibError("Required y attribute is missing in point element.") raise GlifLibError("Required y attribute is missing in point element.")
x = _number(x) x = _number(x)
y = _number(y) y = _number(y)
@ -1106,7 +1109,7 @@ def _buildAnchorFormat1(point):
return anchor return anchor
def _buildOutlineContourFormat1(pen, contour, validate): def _buildOutlineContourFormat1(pen, contour, validate):
if contour.attrib: if validate and contour.attrib:
raise GlifLibError("Unknown attributes in contour element.") raise GlifLibError("Unknown attributes in contour element.")
pen.beginPath() pen.beginPath()
if len(contour): if len(contour):
@ -1123,14 +1126,15 @@ def _buildOutlinePointsFormat1(pen, contour):
name = element.attrib["name"] name = element.attrib["name"]
pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name) pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name)
def _buildOutlineComponentFormat1(pen, component): def _buildOutlineComponentFormat1(pen, component, validate):
if validate:
if len(component): if len(component):
raise GlifLibError("Unknown child elements of component element.") raise GlifLibError("Unknown child elements of component element.")
for attr in component.attrib.keys(): for attr in component.attrib.keys():
if attr not in componentAttributesFormat1: if attr not in componentAttributesFormat1:
raise GlifLibError("Unknown attribute in component element: %s" % attr) raise GlifLibError("Unknown attribute in component element: %s" % attr)
baseGlyphName = component.get("base") baseGlyphName = component.get("base")
if baseGlyphName is None: if validate and baseGlyphName is None:
raise GlifLibError("The base attribute is not defined in the component.") raise GlifLibError("The base attribute is not defined in the component.")
transformation = [] transformation = []
for attr, default in _transformationInfo: for attr, default in _transformationInfo:
@ -1154,14 +1158,16 @@ def buildOutlineFormat2(glyphObject, pen, outline, identifiers, validate):
raise GlifLibError("Unknown element in outline element: %s" % element.tag) raise GlifLibError("Unknown element in outline element: %s" % element.tag)
def _buildOutlineContourFormat2(pen, contour, identifiers, validate): def _buildOutlineContourFormat2(pen, contour, identifiers, validate):
if validate:
for attr in contour.attrib.keys(): for attr in contour.attrib.keys():
if attr not in contourAttributesFormat2: if attr not in contourAttributesFormat2:
raise GlifLibError("Unknown attribute in contour element: %s" % attr) raise GlifLibError("Unknown attribute in contour element: %s" % attr)
identifier = contour.get("identifier") identifier = contour.get("identifier")
if identifier is not None: if identifier is not None:
if validate:
if identifier in identifiers: if identifier in identifiers:
raise GlifLibError("The identifier %s is used more than once." % identifier) raise GlifLibError("The identifier %s is used more than once." % identifier)
if validate and not identifierValidator(identifier): if not identifierValidator(identifier):
raise GlifLibError("The contour identifier %s is not valid." % identifier) raise GlifLibError("The contour identifier %s is not valid." % identifier)
identifiers.add(identifier) identifiers.add(identifier)
try: try:
@ -1183,9 +1189,10 @@ def _buildOutlinePointsFormat2(pen, contour, identifiers, validate):
name = element.attrib["name"] name = element.attrib["name"]
identifier = element.get("identifier") identifier = element.get("identifier")
if identifier is not None: if identifier is not None:
if validate:
if identifier in identifiers: if identifier in identifiers:
raise GlifLibError("The identifier %s is used more than once." % identifier) raise GlifLibError("The identifier %s is used more than once." % identifier)
if validate and not identifierValidator(identifier): if not identifierValidator(identifier):
raise GlifLibError("The identifier %s is not valid." % identifier) raise GlifLibError("The identifier %s is not valid." % identifier)
identifiers.add(identifier) identifiers.add(identifier)
try: try:
@ -1195,13 +1202,14 @@ def _buildOutlinePointsFormat2(pen, contour, identifiers, validate):
warn("The addPoint method needs an identifier kwarg. The point's identifier value has been discarded.", DeprecationWarning) warn("The addPoint method needs an identifier kwarg. The point's identifier value has been discarded.", DeprecationWarning)
def _buildOutlineComponentFormat2(pen, component, identifiers, validate): def _buildOutlineComponentFormat2(pen, component, identifiers, validate):
if validate:
if len(component): if len(component):
raise GlifLibError("Unknown child elements of component element.") raise GlifLibError("Unknown child elements of component element.")
for attr in component.attrib.keys(): for attr in component.attrib.keys():
if attr not in componentAttributesFormat2: if attr not in componentAttributesFormat2:
raise GlifLibError("Unknown attribute in component element: %s" % attr) raise GlifLibError("Unknown attribute in component element: %s" % attr)
baseGlyphName = component.get("base") baseGlyphName = component.get("base")
if baseGlyphName is None: if validate and baseGlyphName is None:
raise GlifLibError("The base attribute is not defined in the component.") raise GlifLibError("The base attribute is not defined in the component.")
transformation = [] transformation = []
for attr, default in _transformationInfo: for attr, default in _transformationInfo:
@ -1213,6 +1221,7 @@ def _buildOutlineComponentFormat2(pen, component, identifiers, validate):
transformation.append(value) transformation.append(value)
identifier = component.get("identifier") identifier = component.get("identifier")
if identifier is not None: if identifier is not None:
if validate:
if identifier in identifiers: if identifier in identifiers:
raise GlifLibError("The identifier %s is used more than once." % identifier) raise GlifLibError("The identifier %s is used more than once." % identifier)
if validate and not identifierValidator(identifier): if validate and not identifierValidator(identifier):
@ -1496,9 +1505,10 @@ class GLIFPointPen(AbstractPointPen):
def beginPath(self, identifier=None, **kwargs): def beginPath(self, identifier=None, **kwargs):
attrs = [] attrs = []
if identifier is not None and self.formatVersion >= 2: if identifier is not None and self.formatVersion >= 2:
if self.validate:
if identifier in self.identifiers: if identifier in self.identifiers:
raise GlifLibError("identifier used more than once: %s" % identifier) raise GlifLibError("identifier used more than once: %s" % identifier)
if self.validate and not identifierValidator(identifier): if not identifierValidator(identifier):
raise GlifLibError("identifier not formatted properly: %s" % identifier) raise GlifLibError("identifier not formatted properly: %s" % identifier)
attrs.append(("identifier", identifier)) attrs.append(("identifier", identifier))
self.identifiers.add(identifier) self.identifiers.add(identifier)
@ -1508,7 +1518,7 @@ class GLIFPointPen(AbstractPointPen):
def endPath(self): def endPath(self):
if self.prevPointTypes and self.prevPointTypes[0] == "move": if self.prevPointTypes and self.prevPointTypes[0] == "move":
if self.prevPointTypes[-1] == "offcurve": if self.validate and self.prevPointTypes[-1] == "offcurve":
raise GlifLibError("open contour has loose offcurve point") raise GlifLibError("open contour has loose offcurve point")
self.writer.endtag("contour") self.writer.endtag("contour")
self.writer.newline() self.writer.newline()
@ -1520,6 +1530,7 @@ class GLIFPointPen(AbstractPointPen):
attrs = [] attrs = []
# coordinates # coordinates
if pt is not None: if pt is not None:
if self.validate:
for coord in pt: for coord in pt:
if not isinstance(coord, (int, float)): if not isinstance(coord, (int, float)):
raise GlifLibError("coordinates must be int or float") raise GlifLibError("coordinates must be int or float")
@ -1528,6 +1539,7 @@ class GLIFPointPen(AbstractPointPen):
# segment type # segment type
if segmentType == "offcurve": if segmentType == "offcurve":
segmentType = None segmentType = None
if self.validate:
if segmentType == "move" and self.prevPointTypes: if segmentType == "move" and self.prevPointTypes:
raise GlifLibError("move occurs after a point has already been added to the contour.") raise GlifLibError("move occurs after a point has already been added to the contour.")
if segmentType in ("move", "line") and self.prevPointTypes and self.prevPointTypes[-1] == "offcurve": if segmentType in ("move", "line") and self.prevPointTypes and self.prevPointTypes[-1] == "offcurve":
@ -1545,7 +1557,7 @@ class GLIFPointPen(AbstractPointPen):
self.prevPointTypes.append(segmentType) self.prevPointTypes.append(segmentType)
# smooth # smooth
if smooth: if smooth:
if segmentType == "offcurve": if self.validate and segmentType == "offcurve":
raise GlifLibError("can't set smooth in an offcurve point.") raise GlifLibError("can't set smooth in an offcurve point.")
attrs.append(("smooth", "yes")) attrs.append(("smooth", "yes"))
# name # name
@ -1553,9 +1565,10 @@ class GLIFPointPen(AbstractPointPen):
attrs.append(("name", name)) attrs.append(("name", name))
# identifier # identifier
if identifier is not None and self.formatVersion >= 2: if identifier is not None and self.formatVersion >= 2:
if self.validate:
if identifier in self.identifiers: if identifier in self.identifiers:
raise GlifLibError("identifier used more than once: %s" % identifier) raise GlifLibError("identifier used more than once: %s" % identifier)
if self.validate and not identifierValidator(identifier): if not identifierValidator(identifier):
raise GlifLibError("identifier not formatted properly: %s" % identifier) raise GlifLibError("identifier not formatted properly: %s" % identifier)
attrs.append(("identifier", identifier)) attrs.append(("identifier", identifier))
self.identifiers.add(identifier) self.identifiers.add(identifier)
@ -1565,11 +1578,12 @@ class GLIFPointPen(AbstractPointPen):
def addComponent(self, glyphName, transformation, identifier=None, **kwargs): def addComponent(self, glyphName, transformation, identifier=None, **kwargs):
attrs = [("base", glyphName)] attrs = [("base", glyphName)]
for (attr, default), value in zip(_transformationInfo, transformation): for (attr, default), value in zip(_transformationInfo, transformation):
if not isinstance(value, (int, float)): if self.validate and not isinstance(value, (int, float)):
raise GlifLibError("transformation values must be int or float") raise GlifLibError("transformation values must be int or float")
if value != default: if value != default:
attrs.append((attr, repr(value))) attrs.append((attr, repr(value)))
if identifier is not None and self.formatVersion >= 2: if identifier is not None and self.formatVersion >= 2:
if self.validate:
if identifier in self.identifiers: if identifier in self.identifiers:
raise GlifLibError("identifier used more than once: %s" % identifier) raise GlifLibError("identifier used more than once: %s" % identifier)
if self.validate and not identifierValidator(identifier): if self.validate and not identifierValidator(identifier):

View File

@ -24,7 +24,7 @@ class TestGLIF1(unittest.TestCase):
py = stripText(py) py = stripText(py)
glyph = Glyph() glyph = Glyph()
exec(py, {"glyph" : glyph, "pointPen" : glyph}) exec(py, {"glyph" : glyph, "pointPen" : glyph})
glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=1) glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=1, validate=True)
glif = "\n".join(glif.splitlines()[1:]) glif = "\n".join(glif.splitlines()[1:])
return glif return glif
@ -32,7 +32,7 @@ class TestGLIF1(unittest.TestCase):
glif = stripText(glif) glif = stripText(glif)
glif = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + glif glif = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + glif
glyph = Glyph() glyph = Glyph()
readGlyphFromString(glif, glyphObject=glyph, pointPen=glyph) readGlyphFromString(glif, glyphObject=glyph, pointPen=glyph, validate=True)
return glyph.py() return glyph.py()
def testTopElement(self): def testTopElement(self):

View File

@ -24,7 +24,7 @@ class TestGLIF2(unittest.TestCase):
py = stripText(py) py = stripText(py)
glyph = Glyph() glyph = Glyph()
exec(py, {"glyph" : glyph, "pointPen" : glyph}) exec(py, {"glyph" : glyph, "pointPen" : glyph})
glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=2) glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=2, validate=True)
glif = "\n".join(glif.splitlines()[1:]) glif = "\n".join(glif.splitlines()[1:])
return glif return glif