diff options
Diffstat (limited to 'Lib/fontTools/ufoLib/validators.py')
-rw-r--r-- | Lib/fontTools/ufoLib/validators.py | 1890 |
1 files changed, 1008 insertions, 882 deletions
diff --git a/Lib/fontTools/ufoLib/validators.py b/Lib/fontTools/ufoLib/validators.py index 49cb0e49..01e3124f 100644 --- a/Lib/fontTools/ufoLib/validators.py +++ b/Lib/fontTools/ufoLib/validators.py @@ -13,6 +13,7 @@ from fontTools.ufoLib.utils import numberTypes # Generic # ------- + def isDictEnough(value): """ Some objects will likely come in that aren't @@ -25,72 +26,78 @@ def isDictEnough(value): return False return True + def genericTypeValidator(value, typ): - """ - Generic. (Added at version 2.) - """ - return isinstance(value, typ) + """ + Generic. (Added at version 2.) + """ + return isinstance(value, typ) + def genericIntListValidator(values, validValues): - """ - Generic. (Added at version 2.) - """ - if not isinstance(values, (list, tuple)): - return False - valuesSet = set(values) - validValuesSet = set(validValues) - if valuesSet - validValuesSet: - return False - for value in values: - if not isinstance(value, int): - return False - return True + """ + Generic. (Added at version 2.) + """ + if not isinstance(values, (list, tuple)): + return False + valuesSet = set(values) + validValuesSet = set(validValues) + if valuesSet - validValuesSet: + return False + for value in values: + if not isinstance(value, int): + return False + return True + def genericNonNegativeIntValidator(value): - """ - Generic. (Added at version 3.) - """ - if not isinstance(value, int): - return False - if value < 0: - return False - return True + """ + Generic. (Added at version 3.) + """ + if not isinstance(value, int): + return False + if value < 0: + return False + return True + def genericNonNegativeNumberValidator(value): - """ - Generic. (Added at version 3.) - """ - if not isinstance(value, numberTypes): - return False - if value < 0: - return False - return True + """ + Generic. (Added at version 3.) + """ + if not isinstance(value, numberTypes): + return False + if value < 0: + return False + return True + def genericDictValidator(value, prototype): - """ - Generic. (Added at version 3.) - """ - # not a dict - if not isinstance(value, Mapping): - return False - # missing required keys - for key, (typ, required) in prototype.items(): - if not required: - continue - if key not in value: - return False - # unknown keys - for key in value.keys(): - if key not in prototype: - return False - # incorrect types - for key, v in value.items(): - prototypeType, required = prototype[key] - if v is None and not required: - continue - if not isinstance(v, prototypeType): - return False - return True + """ + Generic. (Added at version 3.) + """ + # not a dict + if not isinstance(value, Mapping): + return False + # missing required keys + for key, (typ, required) in prototype.items(): + if not required: + continue + if key not in value: + return False + # unknown keys + for key in value.keys(): + if key not in prototype: + return False + # incorrect types + for key, v in value.items(): + prototypeType, required = prototype[key] + if v is None and not required: + continue + if not isinstance(v, prototypeType): + return False + return True + # -------------- # fontinfo.plist @@ -98,620 +105,698 @@ def genericDictValidator(value, prototype): # Data Validators + def fontInfoStyleMapStyleNameValidator(value): - """ - Version 2+. - """ - options = ["regular", "italic", "bold", "bold italic"] - return value in options + """ + Version 2+. + """ + options = ["regular", "italic", "bold", "bold italic"] + return value in options + def fontInfoOpenTypeGaspRangeRecordsValidator(value): - """ - Version 3+. - """ - if not isinstance(value, list): - return False - if len(value) == 0: - return True - validBehaviors = [0, 1, 2, 3] - dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True)) - ppemOrder = [] - for rangeRecord in value: - if not genericDictValidator(rangeRecord, dictPrototype): - return False - ppem = rangeRecord["rangeMaxPPEM"] - behavior = rangeRecord["rangeGaspBehavior"] - ppemValidity = genericNonNegativeIntValidator(ppem) - if not ppemValidity: - return False - behaviorValidity = genericIntListValidator(behavior, validBehaviors) - if not behaviorValidity: - return False - ppemOrder.append(ppem) - if ppemOrder != sorted(ppemOrder): - return False - return True + """ + Version 3+. + """ + if not isinstance(value, list): + return False + if len(value) == 0: + return True + validBehaviors = [0, 1, 2, 3] + dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True)) + ppemOrder = [] + for rangeRecord in value: + if not genericDictValidator(rangeRecord, dictPrototype): + return False + ppem = rangeRecord["rangeMaxPPEM"] + behavior = rangeRecord["rangeGaspBehavior"] + ppemValidity = genericNonNegativeIntValidator(ppem) + if not ppemValidity: + return False + behaviorValidity = genericIntListValidator(behavior, validBehaviors) + if not behaviorValidity: + return False + ppemOrder.append(ppem) + if ppemOrder != sorted(ppemOrder): + return False + return True + def fontInfoOpenTypeHeadCreatedValidator(value): - """ - Version 2+. - """ - # format: 0000/00/00 00:00:00 - if not isinstance(value, str): - return False - # basic formatting - if not len(value) == 19: - return False - if value.count(" ") != 1: - return False - date, time = value.split(" ") - if date.count("/") != 2: - return False - if time.count(":") != 2: - return False - # date - year, month, day = date.split("/") - if len(year) != 4: - return False - if len(month) != 2: - return False - if len(day) != 2: - return False - try: - year = int(year) - month = int(month) - day = int(day) - except ValueError: - return False - if month < 1 or month > 12: - return False - monthMaxDay = calendar.monthrange(year, month)[1] - if day < 1 or day > monthMaxDay: - return False - # time - hour, minute, second = time.split(":") - if len(hour) != 2: - return False - if len(minute) != 2: - return False - if len(second) != 2: - return False - try: - hour = int(hour) - minute = int(minute) - second = int(second) - except ValueError: - return False - if hour < 0 or hour > 23: - return False - if minute < 0 or minute > 59: - return False - if second < 0 or second > 59: - return False - # fallback - return True + """ + Version 2+. + """ + # format: 0000/00/00 00:00:00 + if not isinstance(value, str): + return False + # basic formatting + if not len(value) == 19: + return False + if value.count(" ") != 1: + return False + date, time = value.split(" ") + if date.count("/") != 2: + return False + if time.count(":") != 2: + return False + # date + year, month, day = date.split("/") + if len(year) != 4: + return False + if len(month) != 2: + return False + if len(day) != 2: + return False + try: + year = int(year) + month = int(month) + day = int(day) + except ValueError: + return False + if month < 1 or month > 12: + return False + monthMaxDay = calendar.monthrange(year, month)[1] + if day < 1 or day > monthMaxDay: + return False + # time + hour, minute, second = time.split(":") + if len(hour) != 2: + return False + if len(minute) != 2: + return False + if len(second) != 2: + return False + try: + hour = int(hour) + minute = int(minute) + second = int(second) + except ValueError: + return False + if hour < 0 or hour > 23: + return False + if minute < 0 or minute > 59: + return False + if second < 0 or second > 59: + return False + # fallback + return True + def fontInfoOpenTypeNameRecordsValidator(value): - """ - Version 3+. - """ - if not isinstance(value, list): - return False - dictPrototype = dict(nameID=(int, True), platformID=(int, True), encodingID=(int, True), languageID=(int, True), string=(str, True)) - for nameRecord in value: - if not genericDictValidator(nameRecord, dictPrototype): - return False - return True + """ + Version 3+. + """ + if not isinstance(value, list): + return False + dictPrototype = dict( + nameID=(int, True), + platformID=(int, True), + encodingID=(int, True), + languageID=(int, True), + string=(str, True), + ) + for nameRecord in value: + if not genericDictValidator(nameRecord, dictPrototype): + return False + return True + def fontInfoOpenTypeOS2WeightClassValidator(value): - """ - Version 2+. - """ - if not isinstance(value, int): - return False - if value < 0: - return False - return True + """ + Version 2+. + """ + if not isinstance(value, int): + return False + if value < 0: + return False + return True + def fontInfoOpenTypeOS2WidthClassValidator(value): - """ - Version 2+. - """ - if not isinstance(value, int): - return False - if value < 1: - return False - if value > 9: - return False - return True + """ + Version 2+. + """ + if not isinstance(value, int): + return False + if value < 1: + return False + if value > 9: + return False + return True + def fontInfoVersion2OpenTypeOS2PanoseValidator(values): - """ - Version 2. - """ - if not isinstance(values, (list, tuple)): - return False - if len(values) != 10: - return False - for value in values: - if not isinstance(value, int): - return False - # XXX further validation? - return True + """ + Version 2. + """ + if not isinstance(values, (list, tuple)): + return False + if len(values) != 10: + return False + for value in values: + if not isinstance(value, int): + return False + # XXX further validation? + return True + def fontInfoVersion3OpenTypeOS2PanoseValidator(values): - """ - Version 3+. - """ - if not isinstance(values, (list, tuple)): - return False - if len(values) != 10: - return False - for value in values: - if not isinstance(value, int): - return False - if value < 0: - return False - # XXX further validation? - return True + """ + Version 3+. + """ + if not isinstance(values, (list, tuple)): + return False + if len(values) != 10: + return False + for value in values: + if not isinstance(value, int): + return False + if value < 0: + return False + # XXX further validation? + return True + def fontInfoOpenTypeOS2FamilyClassValidator(values): - """ - Version 2+. - """ - if not isinstance(values, (list, tuple)): - return False - if len(values) != 2: - return False - for value in values: - if not isinstance(value, int): - return False - classID, subclassID = values - if classID < 0 or classID > 14: - return False - if subclassID < 0 or subclassID > 15: - return False - return True + """ + Version 2+. + """ + if not isinstance(values, (list, tuple)): + return False + if len(values) != 2: + return False + for value in values: + if not isinstance(value, int): + return False + classID, subclassID = values + if classID < 0 or classID > 14: + return False + if subclassID < 0 or subclassID > 15: + return False + return True + def fontInfoPostscriptBluesValidator(values): - """ - Version 2+. - """ - if not isinstance(values, (list, tuple)): - return False - if len(values) > 14: - return False - if len(values) % 2: - return False - for value in values: - if not isinstance(value, numberTypes): - return False - return True + """ + Version 2+. + """ + if not isinstance(values, (list, tuple)): + return False + if len(values) > 14: + return False + if len(values) % 2: + return False + for value in values: + if not isinstance(value, numberTypes): + return False + return True + def fontInfoPostscriptOtherBluesValidator(values): - """ - Version 2+. - """ - if not isinstance(values, (list, tuple)): - return False - if len(values) > 10: - return False - if len(values) % 2: - return False - for value in values: - if not isinstance(value, numberTypes): - return False - return True + """ + Version 2+. + """ + if not isinstance(values, (list, tuple)): + return False + if len(values) > 10: + return False + if len(values) % 2: + return False + for value in values: + if not isinstance(value, numberTypes): + return False + return True + def fontInfoPostscriptStemsValidator(values): - """ - Version 2+. - """ - if not isinstance(values, (list, tuple)): - return False - if len(values) > 12: - return False - for value in values: - if not isinstance(value, numberTypes): - return False - return True + """ + Version 2+. + """ + if not isinstance(values, (list, tuple)): + return False + if len(values) > 12: + return False + for value in values: + if not isinstance(value, numberTypes): + return False + return True + def fontInfoPostscriptWindowsCharacterSetValidator(value): - """ - Version 2+. - """ - validValues = list(range(1, 21)) - if value not in validValues: - return False - return True + """ + Version 2+. + """ + validValues = list(range(1, 21)) + if value not in validValues: + return False + return True + def fontInfoWOFFMetadataUniqueIDValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(id=(str, True)) - if not genericDictValidator(value, dictPrototype): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(id=(str, True)) + if not genericDictValidator(value, dictPrototype): + return False + return True + def fontInfoWOFFMetadataVendorValidator(value): - """ - Version 3+. - """ - dictPrototype = {"name" : (str, True), "url" : (str, False), "dir" : (str, False), "class" : (str, False)} - if not genericDictValidator(value, dictPrototype): - return False - if "dir" in value and value.get("dir") not in ("ltr", "rtl"): - return False - return True + """ + Version 3+. + """ + dictPrototype = { + "name": (str, True), + "url": (str, False), + "dir": (str, False), + "class": (str, False), + } + if not genericDictValidator(value, dictPrototype): + return False + if "dir" in value and value.get("dir") not in ("ltr", "rtl"): + return False + return True + def fontInfoWOFFMetadataCreditsValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(credits=(list, True)) - if not genericDictValidator(value, dictPrototype): - return False - if not len(value["credits"]): - return False - dictPrototype = {"name" : (str, True), "url" : (str, False), "role" : (str, False), "dir" : (str, False), "class" : (str, False)} - for credit in value["credits"]: - if not genericDictValidator(credit, dictPrototype): - return False - if "dir" in credit and credit.get("dir") not in ("ltr", "rtl"): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(credits=(list, True)) + if not genericDictValidator(value, dictPrototype): + return False + if not len(value["credits"]): + return False + dictPrototype = { + "name": (str, True), + "url": (str, False), + "role": (str, False), + "dir": (str, False), + "class": (str, False), + } + for credit in value["credits"]: + if not genericDictValidator(credit, dictPrototype): + return False + if "dir" in credit and credit.get("dir") not in ("ltr", "rtl"): + return False + return True + def fontInfoWOFFMetadataDescriptionValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(url=(str, False), text=(list, True)) - if not genericDictValidator(value, dictPrototype): - return False - for text in value["text"]: - if not fontInfoWOFFMetadataTextValue(text): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(url=(str, False), text=(list, True)) + if not genericDictValidator(value, dictPrototype): + return False + for text in value["text"]: + if not fontInfoWOFFMetadataTextValue(text): + return False + return True + def fontInfoWOFFMetadataLicenseValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(url=(str, False), text=(list, False), id=(str, False)) - if not genericDictValidator(value, dictPrototype): - return False - if "text" in value: - for text in value["text"]: - if not fontInfoWOFFMetadataTextValue(text): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(url=(str, False), text=(list, False), id=(str, False)) + if not genericDictValidator(value, dictPrototype): + return False + if "text" in value: + for text in value["text"]: + if not fontInfoWOFFMetadataTextValue(text): + return False + return True + def fontInfoWOFFMetadataTrademarkValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(text=(list, True)) - if not genericDictValidator(value, dictPrototype): - return False - for text in value["text"]: - if not fontInfoWOFFMetadataTextValue(text): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(text=(list, True)) + if not genericDictValidator(value, dictPrototype): + return False + for text in value["text"]: + if not fontInfoWOFFMetadataTextValue(text): + return False + return True + def fontInfoWOFFMetadataCopyrightValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(text=(list, True)) - if not genericDictValidator(value, dictPrototype): - return False - for text in value["text"]: - if not fontInfoWOFFMetadataTextValue(text): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(text=(list, True)) + if not genericDictValidator(value, dictPrototype): + return False + for text in value["text"]: + if not fontInfoWOFFMetadataTextValue(text): + return False + return True + def fontInfoWOFFMetadataLicenseeValidator(value): - """ - Version 3+. - """ - dictPrototype = {"name" : (str, True), "dir" : (str, False), "class" : (str, False)} - if not genericDictValidator(value, dictPrototype): - return False - if "dir" in value and value.get("dir") not in ("ltr", "rtl"): - return False - return True + """ + Version 3+. + """ + dictPrototype = {"name": (str, True), "dir": (str, False), "class": (str, False)} + if not genericDictValidator(value, dictPrototype): + return False + if "dir" in value and value.get("dir") not in ("ltr", "rtl"): + return False + return True + def fontInfoWOFFMetadataTextValue(value): - """ - Version 3+. - """ - dictPrototype = {"text" : (str, True), "language" : (str, False), "dir" : (str, False), "class" : (str, False)} - if not genericDictValidator(value, dictPrototype): - return False - if "dir" in value and value.get("dir") not in ("ltr", "rtl"): - return False - return True + """ + Version 3+. + """ + dictPrototype = { + "text": (str, True), + "language": (str, False), + "dir": (str, False), + "class": (str, False), + } + if not genericDictValidator(value, dictPrototype): + return False + if "dir" in value and value.get("dir") not in ("ltr", "rtl"): + return False + return True + def fontInfoWOFFMetadataExtensionsValidator(value): - """ - Version 3+. - """ - if not isinstance(value, list): - return False - if not value: - return False - for extension in value: - if not fontInfoWOFFMetadataExtensionValidator(extension): - return False - return True + """ + Version 3+. + """ + if not isinstance(value, list): + return False + if not value: + return False + for extension in value: + if not fontInfoWOFFMetadataExtensionValidator(extension): + return False + return True + def fontInfoWOFFMetadataExtensionValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(names=(list, False), items=(list, True), id=(str, False)) - if not genericDictValidator(value, dictPrototype): - return False - if "names" in value: - for name in value["names"]: - if not fontInfoWOFFMetadataExtensionNameValidator(name): - return False - for item in value["items"]: - if not fontInfoWOFFMetadataExtensionItemValidator(item): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(names=(list, False), items=(list, True), id=(str, False)) + if not genericDictValidator(value, dictPrototype): + return False + if "names" in value: + for name in value["names"]: + if not fontInfoWOFFMetadataExtensionNameValidator(name): + return False + for item in value["items"]: + if not fontInfoWOFFMetadataExtensionItemValidator(item): + return False + return True + def fontInfoWOFFMetadataExtensionItemValidator(value): - """ - Version 3+. - """ - dictPrototype = dict(id=(str, False), names=(list, True), values=(list, True)) - if not genericDictValidator(value, dictPrototype): - return False - for name in value["names"]: - if not fontInfoWOFFMetadataExtensionNameValidator(name): - return False - for val in value["values"]: - if not fontInfoWOFFMetadataExtensionValueValidator(val): - return False - return True + """ + Version 3+. + """ + dictPrototype = dict(id=(str, False), names=(list, True), values=(list, True)) + if not genericDictValidator(value, dictPrototype): + return False + for name in value["names"]: + if not fontInfoWOFFMetadataExtensionNameValidator(name): + return False + for val in value["values"]: + if not fontInfoWOFFMetadataExtensionValueValidator(val): + return False + return True + def fontInfoWOFFMetadataExtensionNameValidator(value): - """ - Version 3+. - """ - dictPrototype = {"text" : (str, True), "language" : (str, False), "dir" : (str, False), "class" : (str, False)} - if not genericDictValidator(value, dictPrototype): - return False - if "dir" in value and value.get("dir") not in ("ltr", "rtl"): - return False - return True + """ + Version 3+. + """ + dictPrototype = { + "text": (str, True), + "language": (str, False), + "dir": (str, False), + "class": (str, False), + } + if not genericDictValidator(value, dictPrototype): + return False + if "dir" in value and value.get("dir") not in ("ltr", "rtl"): + return False + return True + def fontInfoWOFFMetadataExtensionValueValidator(value): - """ - Version 3+. - """ - dictPrototype = {"text" : (str, True), "language" : (str, False), "dir" : (str, False), "class" : (str, False)} - if not genericDictValidator(value, dictPrototype): - return False - if "dir" in value and value.get("dir") not in ("ltr", "rtl"): - return False - return True + """ + Version 3+. + """ + dictPrototype = { + "text": (str, True), + "language": (str, False), + "dir": (str, False), + "class": (str, False), + } + if not genericDictValidator(value, dictPrototype): + return False + if "dir" in value and value.get("dir") not in ("ltr", "rtl"): + return False + return True + # ---------- # Guidelines # ---------- + def guidelinesValidator(value, identifiers=None): - """ - Version 3+. - """ - if not isinstance(value, list): - return False - if identifiers is None: - identifiers = set() - for guide in value: - if not guidelineValidator(guide): - return False - identifier = guide.get("identifier") - if identifier is not None: - if identifier in identifiers: - return False - identifiers.add(identifier) - return True + """ + Version 3+. + """ + if not isinstance(value, list): + return False + if identifiers is None: + identifiers = set() + for guide in value: + if not guidelineValidator(guide): + return False + identifier = guide.get("identifier") + if identifier is not None: + if identifier in identifiers: + return False + identifiers.add(identifier) + return True + _guidelineDictPrototype = dict( - x=((int, float), False), y=((int, float), False), angle=((int, float), False), - name=(str, False), color=(str, False), identifier=(str, False) + x=((int, float), False), + y=((int, float), False), + angle=((int, float), False), + name=(str, False), + color=(str, False), + identifier=(str, False), ) + def guidelineValidator(value): - """ - Version 3+. - """ - if not genericDictValidator(value, _guidelineDictPrototype): - return False - x = value.get("x") - y = value.get("y") - angle = value.get("angle") - # x or y must be present - if x is None and y is None: - return False - # if x or y are None, angle must not be present - if x is None or y is None: - if angle is not None: - return False - # if x and y are defined, angle must be defined - if x is not None and y is not None and angle is None: - return False - # angle must be between 0 and 360 - if angle is not None: - if angle < 0: - return False - if angle > 360: - return False - # identifier must be 1 or more characters - identifier = value.get("identifier") - if identifier is not None and not identifierValidator(identifier): - return False - # color must follow the proper format - color = value.get("color") - if color is not None and not colorValidator(color): - return False - return True + """ + Version 3+. + """ + if not genericDictValidator(value, _guidelineDictPrototype): + return False + x = value.get("x") + y = value.get("y") + angle = value.get("angle") + # x or y must be present + if x is None and y is None: + return False + # if x or y are None, angle must not be present + if x is None or y is None: + if angle is not None: + return False + # if x and y are defined, angle must be defined + if x is not None and y is not None and angle is None: + return False + # angle must be between 0 and 360 + if angle is not None: + if angle < 0: + return False + if angle > 360: + return False + # identifier must be 1 or more characters + identifier = value.get("identifier") + if identifier is not None and not identifierValidator(identifier): + return False + # color must follow the proper format + color = value.get("color") + if color is not None and not colorValidator(color): + return False + return True + # ------- # Anchors # ------- + def anchorsValidator(value, identifiers=None): - """ - Version 3+. - """ - if not isinstance(value, list): - return False - if identifiers is None: - identifiers = set() - for anchor in value: - if not anchorValidator(anchor): - return False - identifier = anchor.get("identifier") - if identifier is not None: - if identifier in identifiers: - return False - identifiers.add(identifier) - return True + """ + Version 3+. + """ + if not isinstance(value, list): + return False + if identifiers is None: + identifiers = set() + for anchor in value: + if not anchorValidator(anchor): + return False + identifier = anchor.get("identifier") + if identifier is not None: + if identifier in identifiers: + return False + identifiers.add(identifier) + return True + _anchorDictPrototype = dict( - x=((int, float), False), y=((int, float), False), - name=(str, False), color=(str, False), - identifier=(str, False) + x=((int, float), False), + y=((int, float), False), + name=(str, False), + color=(str, False), + identifier=(str, False), ) + def anchorValidator(value): - """ - Version 3+. - """ - if not genericDictValidator(value, _anchorDictPrototype): - return False - x = value.get("x") - y = value.get("y") - # x and y must be present - if x is None or y is None: - return False - # identifier must be 1 or more characters - identifier = value.get("identifier") - if identifier is not None and not identifierValidator(identifier): - return False - # color must follow the proper format - color = value.get("color") - if color is not None and not colorValidator(color): - return False - return True + """ + Version 3+. + """ + if not genericDictValidator(value, _anchorDictPrototype): + return False + x = value.get("x") + y = value.get("y") + # x and y must be present + if x is None or y is None: + return False + # identifier must be 1 or more characters + identifier = value.get("identifier") + if identifier is not None and not identifierValidator(identifier): + return False + # color must follow the proper format + color = value.get("color") + if color is not None and not colorValidator(color): + return False + return True + # ---------- # Identifier # ---------- + def identifierValidator(value): - """ - Version 3+. - - >>> identifierValidator("a") - True - >>> identifierValidator("") - False - >>> identifierValidator("a" * 101) - False - """ - validCharactersMin = 0x20 - validCharactersMax = 0x7E - if not isinstance(value, str): - return False - if not value: - return False - if len(value) > 100: - return False - for c in value: - c = ord(c) - if c < validCharactersMin or c > validCharactersMax: - return False - return True + """ + Version 3+. + + >>> identifierValidator("a") + True + >>> identifierValidator("") + False + >>> identifierValidator("a" * 101) + False + """ + validCharactersMin = 0x20 + validCharactersMax = 0x7E + if not isinstance(value, str): + return False + if not value: + return False + if len(value) > 100: + return False + for c in value: + c = ord(c) + if c < validCharactersMin or c > validCharactersMax: + return False + return True + # ----- # Color # ----- + def colorValidator(value): - """ - Version 3+. - - >>> colorValidator("0,0,0,0") - True - >>> colorValidator(".5,.5,.5,.5") - True - >>> colorValidator("0.5,0.5,0.5,0.5") - True - >>> colorValidator("1,1,1,1") - True - - >>> colorValidator("2,0,0,0") - False - >>> colorValidator("0,2,0,0") - False - >>> colorValidator("0,0,2,0") - False - >>> colorValidator("0,0,0,2") - False - - >>> colorValidator("1r,1,1,1") - False - >>> colorValidator("1,1g,1,1") - False - >>> colorValidator("1,1,1b,1") - False - >>> colorValidator("1,1,1,1a") - False - - >>> colorValidator("1 1 1 1") - False - >>> colorValidator("1 1,1,1") - False - >>> colorValidator("1,1 1,1") - False - >>> colorValidator("1,1,1 1") - False - - >>> colorValidator("1, 1, 1, 1") - True - """ - if not isinstance(value, str): - return False - parts = value.split(",") - if len(parts) != 4: - return False - for part in parts: - part = part.strip() - converted = False - try: - part = int(part) - converted = True - except ValueError: - pass - if not converted: - try: - part = float(part) - converted = True - except ValueError: - pass - if not converted: - return False - if part < 0: - return False - if part > 1: - return False - return True + """ + Version 3+. + + >>> colorValidator("0,0,0,0") + True + >>> colorValidator(".5,.5,.5,.5") + True + >>> colorValidator("0.5,0.5,0.5,0.5") + True + >>> colorValidator("1,1,1,1") + True + + >>> colorValidator("2,0,0,0") + False + >>> colorValidator("0,2,0,0") + False + >>> colorValidator("0,0,2,0") + False + >>> colorValidator("0,0,0,2") + False + + >>> colorValidator("1r,1,1,1") + False + >>> colorValidator("1,1g,1,1") + False + >>> colorValidator("1,1,1b,1") + False + >>> colorValidator("1,1,1,1a") + False + + >>> colorValidator("1 1 1 1") + False + >>> colorValidator("1 1,1,1") + False + >>> colorValidator("1,1 1,1") + False + >>> colorValidator("1,1,1 1") + False + + >>> colorValidator("1, 1, 1, 1") + True + """ + if not isinstance(value, str): + return False + parts = value.split(",") + if len(parts) != 4: + return False + for part in parts: + part = part.strip() + converted = False + try: + part = int(part) + converted = True + except ValueError: + pass + if not converted: + try: + part = float(part) + converted = True + except ValueError: + pass + if not converted: + return False + if part < 0: + return False + if part > 1: + return False + return True + # ----- # image @@ -720,227 +805,263 @@ def colorValidator(value): pngSignature = b"\x89PNG\r\n\x1a\n" _imageDictPrototype = dict( - fileName=(str, True), - xScale=((int, float), False), xyScale=((int, float), False), - yxScale=((int, float), False), yScale=((int, float), False), - xOffset=((int, float), False), yOffset=((int, float), False), - color=(str, False) + fileName=(str, True), + xScale=((int, float), False), + xyScale=((int, float), False), + yxScale=((int, float), False), + yScale=((int, float), False), + xOffset=((int, float), False), + yOffset=((int, float), False), + color=(str, False), ) + def imageValidator(value): - """ - Version 3+. - """ - if not genericDictValidator(value, _imageDictPrototype): - return False - # fileName must be one or more characters - if not value["fileName"]: - return False - # color must follow the proper format - color = value.get("color") - if color is not None and not colorValidator(color): - return False - return True + """ + Version 3+. + """ + if not genericDictValidator(value, _imageDictPrototype): + return False + # fileName must be one or more characters + if not value["fileName"]: + return False + # color must follow the proper format + color = value.get("color") + if color is not None and not colorValidator(color): + return False + return True + def pngValidator(path=None, data=None, fileObj=None): - """ - Version 3+. - - This checks the signature of the image data. - """ - assert path is not None or data is not None or fileObj is not None - if path is not None: - with open(path, "rb") as f: - signature = f.read(8) - elif data is not None: - signature = data[:8] - elif fileObj is not None: - pos = fileObj.tell() - signature = fileObj.read(8) - fileObj.seek(pos) - if signature != pngSignature: - return False, "Image does not begin with the PNG signature." - return True, None + """ + Version 3+. + + This checks the signature of the image data. + """ + assert path is not None or data is not None or fileObj is not None + if path is not None: + with open(path, "rb") as f: + signature = f.read(8) + elif data is not None: + signature = data[:8] + elif fileObj is not None: + pos = fileObj.tell() + signature = fileObj.read(8) + fileObj.seek(pos) + if signature != pngSignature: + return False, "Image does not begin with the PNG signature." + return True, None + # ------------------- # layercontents.plist # ------------------- + def layerContentsValidator(value, ufoPathOrFileSystem): - """ - Check the validity of layercontents.plist. - Version 3+. - """ - if isinstance(ufoPathOrFileSystem, fs.base.FS): - fileSystem = ufoPathOrFileSystem - else: - fileSystem = fs.osfs.OSFS(ufoPathOrFileSystem) - - bogusFileMessage = "layercontents.plist in not in the correct format." - # file isn't in the right format - if not isinstance(value, list): - return False, bogusFileMessage - # work through each entry - usedLayerNames = set() - usedDirectories = set() - contents = {} - for entry in value: - # layer entry in the incorrect format - if not isinstance(entry, list): - return False, bogusFileMessage - if not len(entry) == 2: - return False, bogusFileMessage - for i in entry: - if not isinstance(i, str): - return False, bogusFileMessage - layerName, directoryName = entry - # check directory naming - if directoryName != "glyphs": - if not directoryName.startswith("glyphs."): - return False, "Invalid directory name (%s) in layercontents.plist." % directoryName - if len(layerName) == 0: - return False, "Empty layer name in layercontents.plist." - # directory doesn't exist - if not fileSystem.exists(directoryName): - return False, "A glyphset does not exist at %s." % directoryName - # default layer name - if layerName == "public.default" and directoryName != "glyphs": - return False, "The name public.default is being used by a layer that is not the default." - # check usage - if layerName in usedLayerNames: - return False, "The layer name %s is used by more than one layer." % layerName - usedLayerNames.add(layerName) - if directoryName in usedDirectories: - return False, "The directory %s is used by more than one layer." % directoryName - usedDirectories.add(directoryName) - # store - contents[layerName] = directoryName - # missing default layer - foundDefault = "glyphs" in contents.values() - if not foundDefault: - return False, "The required default glyph set is not in the UFO." - return True, None + """ + Check the validity of layercontents.plist. + Version 3+. + """ + if isinstance(ufoPathOrFileSystem, fs.base.FS): + fileSystem = ufoPathOrFileSystem + else: + fileSystem = fs.osfs.OSFS(ufoPathOrFileSystem) + + bogusFileMessage = "layercontents.plist in not in the correct format." + # file isn't in the right format + if not isinstance(value, list): + return False, bogusFileMessage + # work through each entry + usedLayerNames = set() + usedDirectories = set() + contents = {} + for entry in value: + # layer entry in the incorrect format + if not isinstance(entry, list): + return False, bogusFileMessage + if not len(entry) == 2: + return False, bogusFileMessage + for i in entry: + if not isinstance(i, str): + return False, bogusFileMessage + layerName, directoryName = entry + # check directory naming + if directoryName != "glyphs": + if not directoryName.startswith("glyphs."): + return ( + False, + "Invalid directory name (%s) in layercontents.plist." + % directoryName, + ) + if len(layerName) == 0: + return False, "Empty layer name in layercontents.plist." + # directory doesn't exist + if not fileSystem.exists(directoryName): + return False, "A glyphset does not exist at %s." % directoryName + # default layer name + if layerName == "public.default" and directoryName != "glyphs": + return ( + False, + "The name public.default is being used by a layer that is not the default.", + ) + # check usage + if layerName in usedLayerNames: + return ( + False, + "The layer name %s is used by more than one layer." % layerName, + ) + usedLayerNames.add(layerName) + if directoryName in usedDirectories: + return ( + False, + "The directory %s is used by more than one layer." % directoryName, + ) + usedDirectories.add(directoryName) + # store + contents[layerName] = directoryName + # missing default layer + foundDefault = "glyphs" in contents.values() + if not foundDefault: + return False, "The required default glyph set is not in the UFO." + return True, None + # ------------ # groups.plist # ------------ + def groupsValidator(value): - """ - Check the validity of the groups. - Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). - - >>> groups = {"A" : ["A", "A"], "A2" : ["A"]} - >>> groupsValidator(groups) - (True, None) - - >>> groups = {"" : ["A"]} - >>> valid, msg = groupsValidator(groups) - >>> valid - False - >>> print(msg) - A group has an empty name. - - >>> groups = {"public.awesome" : ["A"]} - >>> groupsValidator(groups) - (True, None) - - >>> groups = {"public.kern1." : ["A"]} - >>> valid, msg = groupsValidator(groups) - >>> valid - False - >>> print(msg) - The group data contains a kerning group with an incomplete name. - >>> groups = {"public.kern2." : ["A"]} - >>> valid, msg = groupsValidator(groups) - >>> valid - False - >>> print(msg) - The group data contains a kerning group with an incomplete name. - - >>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]} - >>> groupsValidator(groups) - (True, None) - - >>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]} - >>> valid, msg = groupsValidator(groups) - >>> valid - False - >>> print(msg) - The glyph "A" occurs in too many kerning groups. - """ - bogusFormatMessage = "The group data is not in the correct format." - if not isDictEnough(value): - return False, bogusFormatMessage - firstSideMapping = {} - secondSideMapping = {} - for groupName, glyphList in value.items(): - if not isinstance(groupName, (str)): - return False, bogusFormatMessage - if not isinstance(glyphList, (list, tuple)): - return False, bogusFormatMessage - if not groupName: - return False, "A group has an empty name." - if groupName.startswith("public."): - if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."): - # unknown public.* name. silently skip. - continue - else: - if len("public.kernN.") == len(groupName): - return False, "The group data contains a kerning group with an incomplete name." - if groupName.startswith("public.kern1."): - d = firstSideMapping - else: - d = secondSideMapping - for glyphName in glyphList: - if not isinstance(glyphName, str): - return False, "The group data %s contains an invalid member." % groupName - if glyphName in d: - return False, "The glyph \"%s\" occurs in too many kerning groups." % glyphName - d[glyphName] = groupName - return True, None + """ + Check the validity of the groups. + Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). + + >>> groups = {"A" : ["A", "A"], "A2" : ["A"]} + >>> groupsValidator(groups) + (True, None) + + >>> groups = {"" : ["A"]} + >>> valid, msg = groupsValidator(groups) + >>> valid + False + >>> print(msg) + A group has an empty name. + + >>> groups = {"public.awesome" : ["A"]} + >>> groupsValidator(groups) + (True, None) + + >>> groups = {"public.kern1." : ["A"]} + >>> valid, msg = groupsValidator(groups) + >>> valid + False + >>> print(msg) + The group data contains a kerning group with an incomplete name. + >>> groups = {"public.kern2." : ["A"]} + >>> valid, msg = groupsValidator(groups) + >>> valid + False + >>> print(msg) + The group data contains a kerning group with an incomplete name. + + >>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]} + >>> groupsValidator(groups) + (True, None) + + >>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]} + >>> valid, msg = groupsValidator(groups) + >>> valid + False + >>> print(msg) + The glyph "A" occurs in too many kerning groups. + """ + bogusFormatMessage = "The group data is not in the correct format." + if not isDictEnough(value): + return False, bogusFormatMessage + firstSideMapping = {} + secondSideMapping = {} + for groupName, glyphList in value.items(): + if not isinstance(groupName, (str)): + return False, bogusFormatMessage + if not isinstance(glyphList, (list, tuple)): + return False, bogusFormatMessage + if not groupName: + return False, "A group has an empty name." + if groupName.startswith("public."): + if not groupName.startswith("public.kern1.") and not groupName.startswith( + "public.kern2." + ): + # unknown public.* name. silently skip. + continue + else: + if len("public.kernN.") == len(groupName): + return ( + False, + "The group data contains a kerning group with an incomplete name.", + ) + if groupName.startswith("public.kern1."): + d = firstSideMapping + else: + d = secondSideMapping + for glyphName in glyphList: + if not isinstance(glyphName, str): + return ( + False, + "The group data %s contains an invalid member." % groupName, + ) + if glyphName in d: + return ( + False, + 'The glyph "%s" occurs in too many kerning groups.' % glyphName, + ) + d[glyphName] = groupName + return True, None + # ------------- # kerning.plist # ------------- + def kerningValidator(data): - """ - Check the validity of the kerning data structure. - Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). - - >>> kerning = {"A" : {"B" : 100}} - >>> kerningValidator(kerning) - (True, None) - - >>> kerning = {"A" : ["B"]} - >>> valid, msg = kerningValidator(kerning) - >>> valid - False - >>> print(msg) - The kerning data is not in the correct format. - - >>> kerning = {"A" : {"B" : "100"}} - >>> valid, msg = kerningValidator(kerning) - >>> valid - False - >>> print(msg) - The kerning data is not in the correct format. - """ - bogusFormatMessage = "The kerning data is not in the correct format." - if not isinstance(data, Mapping): - return False, bogusFormatMessage - for first, secondDict in data.items(): - if not isinstance(first, str): - return False, bogusFormatMessage - elif not isinstance(secondDict, Mapping): - return False, bogusFormatMessage - for second, value in secondDict.items(): - if not isinstance(second, str): - return False, bogusFormatMessage - elif not isinstance(value, numberTypes): - return False, bogusFormatMessage - return True, None + """ + Check the validity of the kerning data structure. + Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). + + >>> kerning = {"A" : {"B" : 100}} + >>> kerningValidator(kerning) + (True, None) + + >>> kerning = {"A" : ["B"]} + >>> valid, msg = kerningValidator(kerning) + >>> valid + False + >>> print(msg) + The kerning data is not in the correct format. + + >>> kerning = {"A" : {"B" : "100"}} + >>> valid, msg = kerningValidator(kerning) + >>> valid + False + >>> print(msg) + The kerning data is not in the correct format. + """ + bogusFormatMessage = "The kerning data is not in the correct format." + if not isinstance(data, Mapping): + return False, bogusFormatMessage + for first, secondDict in data.items(): + if not isinstance(first, str): + return False, bogusFormatMessage + elif not isinstance(secondDict, Mapping): + return False, bogusFormatMessage + for second, value in secondDict.items(): + if not isinstance(second, str): + return False, bogusFormatMessage + elif not isinstance(value, numberTypes): + return False, bogusFormatMessage + return True, None + # ------------- # lib.plist/lib @@ -948,113 +1069,118 @@ def kerningValidator(data): _bogusLibFormatMessage = "The lib data is not in the correct format: %s" + def fontLibValidator(value): - """ - Check the validity of the lib. - Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). - - >>> lib = {"foo" : "bar"} - >>> fontLibValidator(lib) - (True, None) - - >>> lib = {"public.awesome" : "hello"} - >>> fontLibValidator(lib) - (True, None) - - >>> lib = {"public.glyphOrder" : ["A", "C", "B"]} - >>> fontLibValidator(lib) - (True, None) - - >>> lib = "hello" - >>> valid, msg = fontLibValidator(lib) - >>> valid - False - >>> print(msg) # doctest: +ELLIPSIS - The lib data is not in the correct format: expected a dictionary, ... - - >>> lib = {1: "hello"} - >>> valid, msg = fontLibValidator(lib) - >>> valid - False - >>> print(msg) - The lib key is not properly formatted: expected str, found int: 1 - - >>> lib = {"public.glyphOrder" : "hello"} - >>> valid, msg = fontLibValidator(lib) - >>> valid - False - >>> print(msg) # doctest: +ELLIPSIS - public.glyphOrder is not properly formatted: expected list or tuple,... - - >>> lib = {"public.glyphOrder" : ["A", 1, "B"]} - >>> valid, msg = fontLibValidator(lib) - >>> valid - False - >>> print(msg) # doctest: +ELLIPSIS - public.glyphOrder is not properly formatted: expected str,... - """ - if not isDictEnough(value): - reason = "expected a dictionary, found %s" % type(value).__name__ - return False, _bogusLibFormatMessage % reason - for key, value in value.items(): - if not isinstance(key, str): - return False, ( - "The lib key is not properly formatted: expected str, found %s: %r" % - (type(key).__name__, key)) - # public.glyphOrder - if key == "public.glyphOrder": - bogusGlyphOrderMessage = "public.glyphOrder is not properly formatted: %s" - if not isinstance(value, (list, tuple)): - reason = "expected list or tuple, found %s" % type(value).__name__ - return False, bogusGlyphOrderMessage % reason - for glyphName in value: - if not isinstance(glyphName, str): - reason = "expected str, found %s" % type(glyphName).__name__ - return False, bogusGlyphOrderMessage % reason - return True, None + """ + Check the validity of the lib. + Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). + + >>> lib = {"foo" : "bar"} + >>> fontLibValidator(lib) + (True, None) + + >>> lib = {"public.awesome" : "hello"} + >>> fontLibValidator(lib) + (True, None) + + >>> lib = {"public.glyphOrder" : ["A", "C", "B"]} + >>> fontLibValidator(lib) + (True, None) + + >>> lib = "hello" + >>> valid, msg = fontLibValidator(lib) + >>> valid + False + >>> print(msg) # doctest: +ELLIPSIS + The lib data is not in the correct format: expected a dictionary, ... + + >>> lib = {1: "hello"} + >>> valid, msg = fontLibValidator(lib) + >>> valid + False + >>> print(msg) + The lib key is not properly formatted: expected str, found int: 1 + + >>> lib = {"public.glyphOrder" : "hello"} + >>> valid, msg = fontLibValidator(lib) + >>> valid + False + >>> print(msg) # doctest: +ELLIPSIS + public.glyphOrder is not properly formatted: expected list or tuple,... + + >>> lib = {"public.glyphOrder" : ["A", 1, "B"]} + >>> valid, msg = fontLibValidator(lib) + >>> valid + False + >>> print(msg) # doctest: +ELLIPSIS + public.glyphOrder is not properly formatted: expected str,... + """ + if not isDictEnough(value): + reason = "expected a dictionary, found %s" % type(value).__name__ + return False, _bogusLibFormatMessage % reason + for key, value in value.items(): + if not isinstance(key, str): + return False, ( + "The lib key is not properly formatted: expected str, found %s: %r" + % (type(key).__name__, key) + ) + # public.glyphOrder + if key == "public.glyphOrder": + bogusGlyphOrderMessage = "public.glyphOrder is not properly formatted: %s" + if not isinstance(value, (list, tuple)): + reason = "expected list or tuple, found %s" % type(value).__name__ + return False, bogusGlyphOrderMessage % reason + for glyphName in value: + if not isinstance(glyphName, str): + reason = "expected str, found %s" % type(glyphName).__name__ + return False, bogusGlyphOrderMessage % reason + return True, None + # -------- # GLIF lib # -------- + def glyphLibValidator(value): - """ - Check the validity of the lib. - Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). - - >>> lib = {"foo" : "bar"} - >>> glyphLibValidator(lib) - (True, None) - - >>> lib = {"public.awesome" : "hello"} - >>> glyphLibValidator(lib) - (True, None) - - >>> lib = {"public.markColor" : "1,0,0,0.5"} - >>> glyphLibValidator(lib) - (True, None) - - >>> lib = {"public.markColor" : 1} - >>> valid, msg = glyphLibValidator(lib) - >>> valid - False - >>> print(msg) - public.markColor is not properly formatted. - """ - if not isDictEnough(value): - reason = "expected a dictionary, found %s" % type(value).__name__ - return False, _bogusLibFormatMessage % reason - for key, value in value.items(): - if not isinstance(key, str): - reason = "key (%s) should be a string" % key - return False, _bogusLibFormatMessage % reason - # public.markColor - if key == "public.markColor": - if not colorValidator(value): - return False, "public.markColor is not properly formatted." - return True, None + """ + Check the validity of the lib. + Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). + + >>> lib = {"foo" : "bar"} + >>> glyphLibValidator(lib) + (True, None) + + >>> lib = {"public.awesome" : "hello"} + >>> glyphLibValidator(lib) + (True, None) + + >>> lib = {"public.markColor" : "1,0,0,0.5"} + >>> glyphLibValidator(lib) + (True, None) + + >>> lib = {"public.markColor" : 1} + >>> valid, msg = glyphLibValidator(lib) + >>> valid + False + >>> print(msg) + public.markColor is not properly formatted. + """ + if not isDictEnough(value): + reason = "expected a dictionary, found %s" % type(value).__name__ + return False, _bogusLibFormatMessage % reason + for key, value in value.items(): + if not isinstance(key, str): + reason = "key (%s) should be a string" % key + return False, _bogusLibFormatMessage % reason + # public.markColor + if key == "public.markColor": + if not colorValidator(value): + return False, "public.markColor is not properly formatted." + return True, None if __name__ == "__main__": - import doctest - doctest.testmod() + import doctest + + doctest.testmod() |