Moved the validators to a more accessible location.

git-svn-id: http://svn.robofab.com/branches/ufo3k@289 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c
This commit is contained in:
Tal Leming 2011-09-18 12:24:29 +00:00
parent 2fd7dbbc17
commit b68dfc260d
2 changed files with 706 additions and 681 deletions

View File

@ -33,11 +33,11 @@ fontinfo.plist values between the possible format versions.
import os
import shutil
from cStringIO import StringIO
import calendar
import codecs
from copy import deepcopy
from plistlib import readPlist, writePlist
from glifLib import GlyphSet, READ_MODE, WRITE_MODE
from validators import *
try:
set
@ -308,14 +308,14 @@ class UFOReader(object):
infoDataToSet = _convertFontInfoDataVersion1ToVersion2(infoDataToSet)
# version 2
elif self._formatVersion == 2:
for attr, dataValidationDict in _fontInfoAttributesVersion2ValueData.items():
for attr, dataValidationDict in fontInfoAttributesVersion2ValueData.items():
value = infoDict.get(attr)
if value is None:
continue
infoDataToSet[attr] = value
# version 3
elif self._formatVersion == 3:
for attr, dataValidationDict in _fontInfoAttributesVersion3ValueData.items():
for attr, dataValidationDict in fontInfoAttributesVersion3ValueData.items():
value = infoDict.get(attr)
if value is None:
continue
@ -325,9 +325,9 @@ class UFOReader(object):
raise NotImplementedError
# validate data
if self._formatVersion < 3:
infoDataToSet = _validateInfoVersion2Data(infoDataToSet)
infoDataToSet = validateInfoVersion2Data(infoDataToSet)
elif self._formatVersion == 3:
infoDataToSet = _validateInfoVersion3Data(infoDataToSet)
infoDataToSet = validateInfoVersion3Data(infoDataToSet)
# populate the object
for attr, value in infoDataToSet.items():
try:
@ -677,7 +677,7 @@ class UFOWriter(object):
path = os.path.join(self._path, FONTINFO_FILENAME)
# gather version 3 data
infoData = {}
for attr in _fontInfoAttributesVersion3ValueData.keys():
for attr in fontInfoAttributesVersion3ValueData.keys():
if hasattr(info, attr):
try:
value = getattr(info, attr)
@ -688,13 +688,13 @@ class UFOWriter(object):
infoData[attr] = value
# down convert data if necessary and validate
if self._formatVersion == 3:
infoData = _validateInfoVersion3Data(infoData)
infoData = validateInfoVersion3Data(infoData)
elif self._formatVersion == 2:
infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
infoData = _validateInfoVersion2Data(infoData)
infoData = validateInfoVersion2Data(infoData)
elif self._formatVersion == 1:
infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
infoData = _validateInfoVersion2Data(infoData)
infoData = validateInfoVersion2Data(infoData)
infoData = _convertFontInfoDataVersion2ToVersion1(infoData)
# write file
writePlistAtomically(infoData, path)
@ -836,7 +836,7 @@ def validateFontInfoVersion2ValueForAttribute(attr, value):
a set range of possible values for an attribute, that the
value is in the accepted range.
"""
dataValidationDict = _fontInfoAttributesVersion2ValueData[attr]
dataValidationDict = fontInfoAttributesVersion2ValueData[attr]
valueType = dataValidationDict.get("type")
validator = dataValidationDict.get("valueValidator")
valueOptions = dataValidationDict.get("valueOptions")
@ -845,13 +845,22 @@ def validateFontInfoVersion2ValueForAttribute(attr, value):
isValidValue = validator(value, valueOptions)
# no specific options
else:
if validator == _fontInfoTypeValidator:
if validator == fontInfoTypeValidator:
isValidValue = validator(value, valueType)
else:
isValidValue = validator(value)
return isValidValue
def _validateInfoVersion2Data(infoData):
def validateInfoVersion2Data(infoData):
"""
This performs very basic validation of the value for infoData
following the UFO 2 fontinfo.plist specification. The results
of this should not be interpretted as *correct* for the font
that they are part of. This merely indicates that the values
are of the proper type and, where the specification defines
a set range of possible values for an attribute, that the
value is in the accepted range.
"""
validInfoData = {}
for attr, value in infoData.items():
isValidValue = validateFontInfoVersion2ValueForAttribute(attr, value)
@ -864,14 +873,14 @@ def _validateInfoVersion2Data(infoData):
def validateFontInfoVersion3ValueForAttribute(attr, value):
"""
This performs very basic validation of the value for attribute
following the UFO 2 fontinfo.plist specification. The results
following the UFO 3 fontinfo.plist specification. The results
of this should not be interpretted as *correct* for the font
that they are part of. This merely indicates that the value
is of the proper type and, where the specification defines
a set range of possible values for an attribute, that the
value is in the accepted range.
"""
dataValidationDict = _fontInfoAttributesVersion3ValueData[attr]
dataValidationDict = fontInfoAttributesVersion3ValueData[attr]
valueType = dataValidationDict.get("type")
validator = dataValidationDict.get("valueValidator")
valueOptions = dataValidationDict.get("valueOptions")
@ -880,13 +889,22 @@ def validateFontInfoVersion3ValueForAttribute(attr, value):
isValidValue = validator(value, valueOptions)
# no specific options
else:
if validator == _fontInfoTypeValidator:
if validator == fontInfoTypeValidator:
isValidValue = validator(value, valueType)
else:
isValidValue = validator(value)
return isValidValue
def _validateInfoVersion3Data(infoData):
def validateInfoVersion3Data(infoData):
"""
This performs very basic validation of the value for infoData
following the UFO 3 fontinfo.plist specification. The results
of this should not be interpretted as *correct* for the font
that they are part of. This merely indicates that the values
are of the proper type and, where the specification defines
a set range of possible values for an attribute, that the
value is in the accepted range.
"""
validInfoData = {}
for attr, value in infoData.items():
isValidValue = validateFontInfoVersion3ValueForAttribute(attr, value)
@ -895,623 +913,17 @@ def _validateInfoVersion3Data(infoData):
else:
validInfoData[attr] = value
# handle the kerning prefixes specially
if not _fontInfoKerningPrefixesValidator(infoData):
if not fontInfoKerningPrefixesValidator(infoData):
raise UFOLibError("Invalid kerning prefixes.")
return infoData
# Data Validators
def _fontInfoTypeValidator(value, typ):
"""
Generic. (Added at version 2.)
"""
return isinstance(value, typ)
def _fontInfoIntListValidator(values, validValues):
"""
Generic. (Added at version 2.)
"""
if not isinstance(values, (list, tuple)):
return False
valuesSet = set(values)
validValuesSet = set(validValues)
if len(valuesSet - validValuesSet) > 0:
return False
for value in values:
if not isinstance(value, int):
return False
return True
def _fontInfoNonNegativeIntValidator(value):
"""
Generic. (Added at version 3.)
"""
if not isinstance(value, int):
return False
if value < 0:
return False
return True
def _fontInfoNonNegativeNumberValidator(value):
"""
Generic. (Added at version 3.)
"""
if not isinstance(value, (int, float)):
return False
if value < 0:
return False
return True
def _fontInfoDictValidator(value, prototype):
"""
Generic. (Added at version 3.)
"""
# not a dict
if not isinstance(value, dict):
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 = prototype[key][0]
if not isinstance(v, prototypeType):
return False
return True
def _fontInfoStyleMapStyleNameValidator(value):
"""
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 False
validBehaviors = [0, 1, 2, 3]
dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True))
ppemOrder = []
for rangeRecord in value:
if not _fontInfoDictValidator(rangeRecord, dictPrototype):
return False
ppem = rangeRecord["rangeMaxPPEM"]
behavior = rangeRecord["rangeGaspBehavior"]
ppemValidity = _fontInfoNonNegativeIntValidator(ppem)
if not ppemValidity:
return False
behaviorValidity = _fontInfoIntListValidator(behavior, validBehaviors)
if not behaviorValidity:
return False
ppemOrder.append(ppem)
if ppemOrder != sorted(ppemOrder):
return False
if ppemOrder[-1] != 0xFFFF:
return False
return True
def _fontInfoOpenTypeHeadCreatedValidator(value):
"""
Version 2+.
"""
# format: 0000/00/00 00:00:00
if not isinstance(value, basestring):
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)
if month > 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 True
# fallback
return True
def _fontInfoOpenTypeNameRecordsValidator(value):
"""
Version 3+.
"""
if not isinstance(value, list):
return False
validKeys = set(["nameID", "platformID", "encodingID", "languageID", "string"])
dictPrototype = dict(nameID=(int, True), platformID=(int, True), encodingID=(int, True), languageID=(int, True), string=(basestring, True))
seenRecords = []
for nameRecord in value:
if not _fontInfoDictValidator(nameRecord, dictPrototype):
return False
recordKey = (nameRecord["nameID"], nameRecord["platformID"], nameRecord["encodingID"], nameRecord["languageID"])
if recordKey in seenRecords:
return False
seenRecords.append(recordKey)
return True
def _fontInfoOpenTypeOS2WeightClassValidator(value):
"""
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
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
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
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
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, (int, float)):
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, (int, float)):
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, (int, float)):
return False
return True
def _fontInfoPostscriptWindowsCharacterSetValidator(value):
"""
Version 2+.
"""
validValues = range(1, 21)
if value not in validValues:
return False
return True
def _fontInfoWOFFMetadataUniqueIDValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(id=(basestring, True))
if not _fontInfoDictValidator(value, dictPrototype):
return False
return True
def _fontInfoWOFFMetadataVendorValidator(value):
"""
Version 3+.
"""
dictPrototype = {"name" : (basestring, True), "url" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not _fontInfoDictValidator(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 _fontInfoDictValidator(value, dictPrototype):
return False
if not len(value["credits"]):
return False
dictPrototype = {"name" : (basestring, True), "url" : (basestring, False), "role" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
for credit in value["credits"]:
if not _fontInfoDictValidator(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=(basestring, False), text=(list, True))
if not _fontInfoDictValidator(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=(basestring, False), text=(list, False), id=(basestring, False))
if not _fontInfoDictValidator(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 _fontInfoDictValidator(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 _fontInfoDictValidator(value, dictPrototype):
return False
for text in value["text"]:
if not _fontInfoWOFFMetadataTextValue(text):
return False
return True
def _fontInfoWOFFMetadataLicenseeValidator(value):
"""
Version 3+.
"""
dictPrototype = {"name" : (basestring, True), "dir" : (basestring, False), "class" : (basestring, False)}
if not _fontInfoDictValidator(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" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not _fontInfoDictValidator(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
def _fontInfoWOFFMetadataExtensionValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(names=(list, False), items=(list, True))
if not _fontInfoDictValidator(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=(basestring, False), names=(list, True), values=(list, True))
if not _fontInfoDictValidator(value, dictPrototype):
return False
for name in value["names"]:
if not _fontInfoWOFFMetadataExtensionNameValidator(name):
return False
for value in value["values"]:
if not _fontInfoWOFFMetadataExtensionValueValidator(name):
return False
return True
def _fontInfoWOFFMetadataExtensionNameValidator(value):
"""
Version 3+.
"""
dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not _fontInfoDictValidator(value, dictPrototype):
return False
return True
def _fontInfoWOFFMetadataExtensionValueValidator(value):
"""
Version 3+.
"""
dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not _fontInfoDictValidator(value, dictPrototype):
return False
return True
def _fontInfoKerningPrefixesValidator(info):
"""
Version 3+.
"""
prefix1 = info.get("firstKerningGroupPrefix")
prefix2 = info.get("secondKerningGroupPrefix")
# both are None
if prefix1 is None and prefix2 is None:
return True
# one is None
if prefix1 is None and prefix2 is not None:
return False
if prefix2 is None and prefix1 is not None:
return False
# they are the same
if prefix1 == prefix2:
return False
# one starts with the other
if prefix1.startswith(prefix2):
return False
if prefix2.startswith(prefix1):
return False
return True
def _fontInfoKerningPrefixValidator(value):
"""
Version 3+.
"""
if not isinstance(value, basestring):
return False
if not len(value):
return False
return True
def _fontInfoGuidelinesValidator(value):
"""
Version 3+.
"""
if not isinstance(value, list):
return True
identifiers = set()
for guide in value:
if not _fontInfoGuidelineValidator(guide):
return False
identifier = guide.get("identifier")
if identifier is not None:
if identifier in identifiers:
return False
identifiers.add(identifier)
return True
def _fontInfoGuidelineValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(
x=((int, float), False), y=((int, float), False), angle=((int, float), False),
name=(basestring, False), color=(basestring, False), identifier=(basestring, False)
)
if not _fontInfoDictValidator(value, dictPrototype):
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
# 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 _fontInfoIdentifierValidator(identifier):
return False
# color must follow the proper format
color = value.get("color")
if color is not None and not _fontInfoColorValidator(color):
return False
return True
def _fontInfoIdentifierValidator(value):
"""
Version 3+.
"""
validCharactersMin = 0x20
validCharactersMax = 0x7E
if not isinstance(value, basestring):
return False
if not value:
return False
for c in value:
c = ord(c)
if c < validCharactersMin or c > validCharactersMax:
return False
return True
def _fontInfoColorValidator(value):
"""
Version 3+.
"""
if not isinstance(value, basestring):
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
# Value Options
_fontInfoOpenTypeHeadFlagsOptions = range(0, 14)
_fontInfoOpenTypeOS2SelectionOptions = [1, 2, 3, 4]
_fontInfoOpenTypeOS2UnicodeRangesOptions = range(0, 128)
_fontInfoOpenTypeOS2CodePageRangesOptions = range(0, 64)
_fontInfoOpenTypeOS2TypeOptions = [0, 1, 2, 3, 8, 9]
fontInfoOpenTypeHeadFlagsOptions = range(0, 14)
fontInfoOpenTypeOS2SelectionOptions = [1, 2, 3, 4]
fontInfoOpenTypeOS2UnicodeRangesOptions = range(0, 128)
fontInfoOpenTypeOS2CodePageRangesOptions = range(0, 64)
fontInfoOpenTypeOS2TypeOptions = [0, 1, 2, 3, 8, 9]
# Version Attribute Definitions
# This defines the attributes, types and, in some
@ -1561,11 +973,11 @@ fontInfoAttributesVersion1 = set([
"ttVersion",
])
_fontInfoAttributesVersion2ValueData = {
fontInfoAttributesVersion2ValueData = {
"familyName" : dict(type=basestring),
"styleName" : dict(type=basestring),
"styleMapFamilyName" : dict(type=basestring),
"styleMapStyleName" : dict(type=basestring, valueValidator=_fontInfoStyleMapStyleNameValidator),
"styleMapStyleName" : dict(type=basestring, valueValidator=fontInfoStyleMapStyleNameValidator),
"versionMajor" : dict(type=int),
"versionMinor" : dict(type=int),
"year" : dict(type=int),
@ -1578,9 +990,9 @@ _fontInfoAttributesVersion2ValueData = {
"ascender" : dict(type=(int, float)),
"italicAngle" : dict(type=(float, int)),
"note" : dict(type=basestring),
"openTypeHeadCreated" : dict(type=basestring, valueValidator=_fontInfoOpenTypeHeadCreatedValidator),
"openTypeHeadCreated" : dict(type=basestring, valueValidator=fontInfoOpenTypeHeadCreatedValidator),
"openTypeHeadLowestRecPPEM" : dict(type=(int, float)),
"openTypeHeadFlags" : dict(type="integerList", valueValidator=_fontInfoIntListValidator, valueOptions=_fontInfoOpenTypeHeadFlagsOptions),
"openTypeHeadFlags" : dict(type="integerList", valueValidator=fontInfoIntListValidator, valueOptions=fontInfoOpenTypeHeadFlagsOptions),
"openTypeHheaAscender" : dict(type=(int, float)),
"openTypeHheaDescender" : dict(type=(int, float)),
"openTypeHheaLineGap" : dict(type=(int, float)),
@ -1602,20 +1014,20 @@ _fontInfoAttributesVersion2ValueData = {
"openTypeNameSampleText" : dict(type=basestring),
"openTypeNameWWSFamilyName" : dict(type=basestring),
"openTypeNameWWSSubfamilyName" : dict(type=basestring),
"openTypeOS2WidthClass" : dict(type=int, valueValidator=_fontInfoOpenTypeOS2WidthClassValidator),
"openTypeOS2WeightClass" : dict(type=int, valueValidator=_fontInfoOpenTypeOS2WeightClassValidator),
"openTypeOS2Selection" : dict(type="integerList", valueValidator=_fontInfoIntListValidator, valueOptions=_fontInfoOpenTypeOS2SelectionOptions),
"openTypeOS2WidthClass" : dict(type=int, valueValidator=fontInfoOpenTypeOS2WidthClassValidator),
"openTypeOS2WeightClass" : dict(type=int, valueValidator=fontInfoOpenTypeOS2WeightClassValidator),
"openTypeOS2Selection" : dict(type="integerList", valueValidator=fontInfoIntListValidator, valueOptions=fontInfoOpenTypeOS2SelectionOptions),
"openTypeOS2VendorID" : dict(type=basestring),
"openTypeOS2Panose" : dict(type="integerList", valueValidator=_fontInfoVersion2OpenTypeOS2PanoseValidator),
"openTypeOS2FamilyClass" : dict(type="integerList", valueValidator=_fontInfoOpenTypeOS2FamilyClassValidator),
"openTypeOS2UnicodeRanges" : dict(type="integerList", valueValidator=_fontInfoIntListValidator, valueOptions=_fontInfoOpenTypeOS2UnicodeRangesOptions),
"openTypeOS2CodePageRanges" : dict(type="integerList", valueValidator=_fontInfoIntListValidator, valueOptions=_fontInfoOpenTypeOS2CodePageRangesOptions),
"openTypeOS2Panose" : dict(type="integerList", valueValidator=fontInfoVersion2OpenTypeOS2PanoseValidator),
"openTypeOS2FamilyClass" : dict(type="integerList", valueValidator=fontInfoOpenTypeOS2FamilyClassValidator),
"openTypeOS2UnicodeRanges" : dict(type="integerList", valueValidator=fontInfoIntListValidator, valueOptions=fontInfoOpenTypeOS2UnicodeRangesOptions),
"openTypeOS2CodePageRanges" : dict(type="integerList", valueValidator=fontInfoIntListValidator, valueOptions=fontInfoOpenTypeOS2CodePageRangesOptions),
"openTypeOS2TypoAscender" : dict(type=(int, float)),
"openTypeOS2TypoDescender" : dict(type=(int, float)),
"openTypeOS2TypoLineGap" : dict(type=(int, float)),
"openTypeOS2WinAscent" : dict(type=(int, float)),
"openTypeOS2WinDescent" : dict(type=(int, float)),
"openTypeOS2Type" : dict(type="integerList", valueValidator=_fontInfoIntListValidator, valueOptions=_fontInfoOpenTypeOS2TypeOptions),
"openTypeOS2Type" : dict(type="integerList", valueValidator=fontInfoIntListValidator, valueOptions=fontInfoOpenTypeOS2TypeOptions),
"openTypeOS2SubscriptXSize" : dict(type=(int, float)),
"openTypeOS2SubscriptYSize" : dict(type=(int, float)),
"openTypeOS2SubscriptXOffset" : dict(type=(int, float)),
@ -1639,12 +1051,12 @@ _fontInfoAttributesVersion2ValueData = {
"postscriptUnderlineThickness" : dict(type=(int, float)),
"postscriptUnderlinePosition" : dict(type=(int, float)),
"postscriptIsFixedPitch" : dict(type=bool),
"postscriptBlueValues" : dict(type="integerList", valueValidator=_fontInfoPostscriptBluesValidator),
"postscriptOtherBlues" : dict(type="integerList", valueValidator=_fontInfoPostscriptOtherBluesValidator),
"postscriptFamilyBlues" : dict(type="integerList", valueValidator=_fontInfoPostscriptBluesValidator),
"postscriptFamilyOtherBlues" : dict(type="integerList", valueValidator=_fontInfoPostscriptOtherBluesValidator),
"postscriptStemSnapH" : dict(type="integerList", valueValidator=_fontInfoPostscriptStemsValidator),
"postscriptStemSnapV" : dict(type="integerList", valueValidator=_fontInfoPostscriptStemsValidator),
"postscriptBlueValues" : dict(type="integerList", valueValidator=fontInfoPostscriptBluesValidator),
"postscriptOtherBlues" : dict(type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator),
"postscriptFamilyBlues" : dict(type="integerList", valueValidator=fontInfoPostscriptBluesValidator),
"postscriptFamilyOtherBlues" : dict(type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator),
"postscriptStemSnapH" : dict(type="integerList", valueValidator=fontInfoPostscriptStemsValidator),
"postscriptStemSnapV" : dict(type="integerList", valueValidator=fontInfoPostscriptStemsValidator),
"postscriptBlueFuzz" : dict(type=(int, float)),
"postscriptBlueShift" : dict(type=(int, float)),
"postscriptBlueScale" : dict(type=(float, int)),
@ -1653,47 +1065,47 @@ _fontInfoAttributesVersion2ValueData = {
"postscriptNominalWidthX" : dict(type=(int, float)),
"postscriptWeightName" : dict(type=basestring),
"postscriptDefaultCharacter" : dict(type=basestring),
"postscriptWindowsCharacterSet" : dict(type=int, valueValidator=_fontInfoPostscriptWindowsCharacterSetValidator),
"postscriptWindowsCharacterSet" : dict(type=int, valueValidator=fontInfoPostscriptWindowsCharacterSetValidator),
"macintoshFONDFamilyID" : dict(type=int),
"macintoshFONDName" : dict(type=basestring),
}
fontInfoAttributesVersion2 = set(_fontInfoAttributesVersion2ValueData.keys())
fontInfoAttributesVersion2 = set(fontInfoAttributesVersion2ValueData.keys())
_fontInfoAttributesVersion3ValueData = deepcopy(_fontInfoAttributesVersion2ValueData)
_fontInfoAttributesVersion3ValueData.update({
"versionMinor" : dict(type=int, valueValidator=_fontInfoNonNegativeIntValidator),
"unitsPerEm" : dict(type=(int, float), valueValidator=_fontInfoNonNegativeNumberValidator),
"openTypeHeadLowestRecPPEM" : dict(type=(int, float), valueValidator=_fontInfoNonNegativeNumberValidator),
"openTypeOS2Panose" : dict(type="integerList", valueValidator=_fontInfoVersion3OpenTypeOS2PanoseValidator),
"openTypeOS2WinAscent" : dict(type=(int, float), valueValidator=_fontInfoNonNegativeNumberValidator),
"openTypeOS2WinDescent" : dict(type=(int, float), valueValidator=_fontInfoNonNegativeNumberValidator),
"openTypeGaspRangeRecords" : dict(type="dictList", valueValidator=_fontInfoOpenTypeGaspRangeRecordsValidator),
"openTypeNameRecords" : dict(type="dictList", valueValidator=_fontInfoOpenTypeNameRecordsValidator),
"woffMajorVersion" : dict(type=int, valueValidator=_fontInfoNonNegativeIntValidator),
"woffMinorVersion" : dict(type=int, valueValidator=_fontInfoNonNegativeIntValidator),
"woffMetadataUniqueID" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataUniqueIDValidator),
"woffMetadataVendor" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataVendorValidator),
"woffMetadataCredits" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataCreditsValidator),
"woffMetadataDescription" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataDescriptionValidator),
"woffMetadataLicense" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataLicenseValidator),
"woffMetadataCopyright" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataCopyrightValidator),
"woffMetadataTrademark" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataTrademarkValidator),
"woffMetadataLicensee" : dict(type=dict, valueValidator=_fontInfoWOFFMetadataLicenseeValidator),
"woffMetadataExtensions" : dict(type=list, valueValidator=_fontInfoWOFFMetadataExtensionsValidator),
"firstKerningGroupPrefix" : dict(type=basestring, valueValidator=_fontInfoKerningPrefixValidator),
"secondKerningGroupPrefix" : dict(type=basestring, valueValidator=_fontInfoKerningPrefixValidator),
"guidelines" : dict(type=list, valueValidator=_fontInfoGuidelinesValidator)
fontInfoAttributesVersion3ValueData = deepcopy(fontInfoAttributesVersion2ValueData)
fontInfoAttributesVersion3ValueData.update({
"versionMinor" : dict(type=int, valueValidator=fontInfoNonNegativeIntValidator),
"unitsPerEm" : dict(type=(int, float), valueValidator=fontInfoNonNegativeNumberValidator),
"openTypeHeadLowestRecPPEM" : dict(type=(int, float), valueValidator=fontInfoNonNegativeNumberValidator),
"openTypeOS2Panose" : dict(type="integerList", valueValidator=fontInfoVersion3OpenTypeOS2PanoseValidator),
"openTypeOS2WinAscent" : dict(type=(int, float), valueValidator=fontInfoNonNegativeNumberValidator),
"openTypeOS2WinDescent" : dict(type=(int, float), valueValidator=fontInfoNonNegativeNumberValidator),
"openTypeGaspRangeRecords" : dict(type="dictList", valueValidator=fontInfoOpenTypeGaspRangeRecordsValidator),
"openTypeNameRecords" : dict(type="dictList", valueValidator=fontInfoOpenTypeNameRecordsValidator),
"woffMajorVersion" : dict(type=int, valueValidator=fontInfoNonNegativeIntValidator),
"woffMinorVersion" : dict(type=int, valueValidator=fontInfoNonNegativeIntValidator),
"woffMetadataUniqueID" : dict(type=dict, valueValidator=fontInfoWOFFMetadataUniqueIDValidator),
"woffMetadataVendor" : dict(type=dict, valueValidator=fontInfoWOFFMetadataVendorValidator),
"woffMetadataCredits" : dict(type=dict, valueValidator=fontInfoWOFFMetadataCreditsValidator),
"woffMetadataDescription" : dict(type=dict, valueValidator=fontInfoWOFFMetadataDescriptionValidator),
"woffMetadataLicense" : dict(type=dict, valueValidator=fontInfoWOFFMetadataLicenseValidator),
"woffMetadataCopyright" : dict(type=dict, valueValidator=fontInfoWOFFMetadataCopyrightValidator),
"woffMetadataTrademark" : dict(type=dict, valueValidator=fontInfoWOFFMetadataTrademarkValidator),
"woffMetadataLicensee" : dict(type=dict, valueValidator=fontInfoWOFFMetadataLicenseeValidator),
"woffMetadataExtensions" : dict(type=list, valueValidator=fontInfoWOFFMetadataExtensionsValidator),
"firstKerningGroupPrefix" : dict(type=basestring, valueValidator=fontInfoKerningPrefixValidator),
"secondKerningGroupPrefix" : dict(type=basestring, valueValidator=fontInfoKerningPrefixValidator),
"guidelines" : dict(type=list, valueValidator=fontInfoGuidelinesValidator)
})
# insert the type validator for all attrs that
# have no defined validator.
for attr, dataDict in _fontInfoAttributesVersion2ValueData.items():
for attr, dataDict in fontInfoAttributesVersion2ValueData.items():
if "valueValidator" not in dataDict:
dataDict["valueValidator"] = _fontInfoTypeValidator
dataDict["valueValidator"] = fontInfoTypeValidator
for attr, dataDict in _fontInfoAttributesVersion3ValueData.items():
for attr, dataDict in fontInfoAttributesVersion3ValueData.items():
if "valueValidator" not in dataDict:
dataDict["valueValidator"] = _fontInfoTypeValidator
dataDict["valueValidator"] = fontInfoTypeValidator
# Version Conversion Support
# These are used from converting from version 1
@ -1705,7 +1117,7 @@ def _flipDict(d):
flipped[value] = key
return flipped
_fontInfoAttributesVersion1To2 = {
fontInfoAttributesVersion1To2 = {
"menuName" : "styleMapFamilyName",
"designer" : "openTypeNameDesigner",
"designerURL" : "openTypeNameDesignerURL",
@ -1734,8 +1146,8 @@ _fontInfoAttributesVersion1To2 = {
"widthName" : "openTypeOS2WidthClass",
"msCharSet" : "postscriptWindowsCharacterSet"
}
_fontInfoAttributesVersion2To1 = _flipDict(_fontInfoAttributesVersion1To2)
deprecatedFontInfoAttributesVersion2 = set(_fontInfoAttributesVersion1To2.keys())
fontInfoAttributesVersion2To1 = _flipDict(fontInfoAttributesVersion1To2)
deprecatedFontInfoAttributesVersion2 = set(fontInfoAttributesVersion1To2.keys())
_fontStyle1To2 = {
64 : "regular",
@ -1820,7 +1232,7 @@ def convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value):
if v is None:
raise UFOLibError("Cannot convert value (%s) for attribute %s." % (repr(value), attr))
value = v
attr = _fontInfoAttributesVersion1To2.get(attr, attr)
attr = fontInfoAttributesVersion1To2.get(attr, attr)
return attr, value
def convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value):
@ -1836,7 +1248,7 @@ def convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value):
value = _widthName2To1.get(value)
elif attr == "postscriptWindowsCharacterSet":
value = _msCharSet2To1.get(value)
attr = _fontInfoAttributesVersion2To1.get(attr, attr)
attr = fontInfoAttributesVersion2To1.get(attr, attr)
return attr, value
def _convertFontInfoDataVersion1ToVersion2(data):

613
Lib/ufoLib/validators.py Normal file
View File

@ -0,0 +1,613 @@
"""Various low level data validators."""
import calendar
# ----------------------
# fontinfo.plist Support
# ----------------------
# Data Validators
def fontInfoTypeValidator(value, typ):
"""
Generic. (Added at version 2.)
"""
return isinstance(value, typ)
def fontInfoIntListValidator(values, validValues):
"""
Generic. (Added at version 2.)
"""
if not isinstance(values, (list, tuple)):
return False
valuesSet = set(values)
validValuesSet = set(validValues)
if len(valuesSet - validValuesSet) > 0:
return False
for value in values:
if not isinstance(value, int):
return False
return True
def fontInfoNonNegativeIntValidator(value):
"""
Generic. (Added at version 3.)
"""
if not isinstance(value, int):
return False
if value < 0:
return False
return True
def fontInfoNonNegativeNumberValidator(value):
"""
Generic. (Added at version 3.)
"""
if not isinstance(value, (int, float)):
return False
if value < 0:
return False
return True
def fontInfoDictValidator(value, prototype):
"""
Generic. (Added at version 3.)
"""
# not a dict
if not isinstance(value, dict):
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 = prototype[key][0]
if not isinstance(v, prototypeType):
return False
return True
def fontInfoStyleMapStyleNameValidator(value):
"""
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 False
validBehaviors = [0, 1, 2, 3]
dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True))
ppemOrder = []
for rangeRecord in value:
if not fontInfoDictValidator(rangeRecord, dictPrototype):
return False
ppem = rangeRecord["rangeMaxPPEM"]
behavior = rangeRecord["rangeGaspBehavior"]
ppemValidity = fontInfoNonNegativeIntValidator(ppem)
if not ppemValidity:
return False
behaviorValidity = fontInfoIntListValidator(behavior, validBehaviors)
if not behaviorValidity:
return False
ppemOrder.append(ppem)
if ppemOrder != sorted(ppemOrder):
return False
if ppemOrder[-1] != 0xFFFF:
return False
return True
def fontInfoOpenTypeHeadCreatedValidator(value):
"""
Version 2+.
"""
# format: 0000/00/00 00:00:00
if not isinstance(value, basestring):
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)
if month > 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 True
# fallback
return True
def fontInfoOpenTypeNameRecordsValidator(value):
"""
Version 3+.
"""
if not isinstance(value, list):
return False
validKeys = set(["nameID", "platformID", "encodingID", "languageID", "string"])
dictPrototype = dict(nameID=(int, True), platformID=(int, True), encodingID=(int, True), languageID=(int, True), string=(basestring, True))
seenRecords = []
for nameRecord in value:
if not fontInfoDictValidator(nameRecord, dictPrototype):
return False
recordKey = (nameRecord["nameID"], nameRecord["platformID"], nameRecord["encodingID"], nameRecord["languageID"])
if recordKey in seenRecords:
return False
seenRecords.append(recordKey)
return True
def fontInfoOpenTypeOS2WeightClassValidator(value):
"""
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
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
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
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
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, (int, float)):
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, (int, float)):
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, (int, float)):
return False
return True
def fontInfoPostscriptWindowsCharacterSetValidator(value):
"""
Version 2+.
"""
validValues = range(1, 21)
if value not in validValues:
return False
return True
def fontInfoWOFFMetadataUniqueIDValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(id=(basestring, True))
if not fontInfoDictValidator(value, dictPrototype):
return False
return True
def fontInfoWOFFMetadataVendorValidator(value):
"""
Version 3+.
"""
dictPrototype = {"name" : (basestring, True), "url" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not fontInfoDictValidator(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 fontInfoDictValidator(value, dictPrototype):
return False
if not len(value["credits"]):
return False
dictPrototype = {"name" : (basestring, True), "url" : (basestring, False), "role" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
for credit in value["credits"]:
if not fontInfoDictValidator(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=(basestring, False), text=(list, True))
if not fontInfoDictValidator(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=(basestring, False), text=(list, False), id=(basestring, False))
if not fontInfoDictValidator(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 fontInfoDictValidator(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 fontInfoDictValidator(value, dictPrototype):
return False
for text in value["text"]:
if not fontInfoWOFFMetadataTextValue(text):
return False
return True
def fontInfoWOFFMetadataLicenseeValidator(value):
"""
Version 3+.
"""
dictPrototype = {"name" : (basestring, True), "dir" : (basestring, False), "class" : (basestring, False)}
if not fontInfoDictValidator(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" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not fontInfoDictValidator(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
def fontInfoWOFFMetadataExtensionValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(names=(list, False), items=(list, True))
if not fontInfoDictValidator(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=(basestring, False), names=(list, True), values=(list, True))
if not fontInfoDictValidator(value, dictPrototype):
return False
for name in value["names"]:
if not fontInfoWOFFMetadataExtensionNameValidator(name):
return False
for value in value["values"]:
if not fontInfoWOFFMetadataExtensionValueValidator(name):
return False
return True
def fontInfoWOFFMetadataExtensionNameValidator(value):
"""
Version 3+.
"""
dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not fontInfoDictValidator(value, dictPrototype):
return False
return True
def fontInfoWOFFMetadataExtensionValueValidator(value):
"""
Version 3+.
"""
dictPrototype = {"text" : (basestring, True), "language" : (basestring, False), "dir" : (basestring, False), "class" : (basestring, False)}
if not fontInfoDictValidator(value, dictPrototype):
return False
return True
def fontInfoKerningPrefixesValidator(info):
"""
Version 3+.
"""
prefix1 = info.get("firstKerningGroupPrefix")
prefix2 = info.get("secondKerningGroupPrefix")
# both are None
if prefix1 is None and prefix2 is None:
return True
# one is None
if prefix1 is None and prefix2 is not None:
return False
if prefix2 is None and prefix1 is not None:
return False
# they are the same
if prefix1 == prefix2:
return False
# one starts with the other
if prefix1.startswith(prefix2):
return False
if prefix2.startswith(prefix1):
return False
return True
def fontInfoKerningPrefixValidator(value):
"""
Version 3+.
"""
if not isinstance(value, basestring):
return False
if not len(value):
return False
return True
def fontInfoGuidelinesValidator(value):
"""
Version 3+.
"""
if not isinstance(value, list):
return True
identifiers = set()
for guide in value:
if not fontInfoGuidelineValidator(guide):
return False
identifier = guide.get("identifier")
if identifier is not None:
if identifier in identifiers:
return False
identifiers.add(identifier)
return True
def fontInfoGuidelineValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(
x=((int, float), False), y=((int, float), False), angle=((int, float), False),
name=(basestring, False), color=(basestring, False), identifier=(basestring, False)
)
if not fontInfoDictValidator(value, dictPrototype):
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
# 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 fontInfoIdentifierValidator(identifier):
return False
# color must follow the proper format
color = value.get("color")
if color is not None and not fontInfoColorValidator(color):
return False
return True
def fontInfoIdentifierValidator(value):
"""
Version 3+.
"""
validCharactersMin = 0x20
validCharactersMax = 0x7E
if not isinstance(value, basestring):
return False
if not value:
return False
for c in value:
c = ord(c)
if c < validCharactersMin or c > validCharactersMax:
return False
return True
def fontInfoColorValidator(value):
"""
Version 3+.
"""
if not isinstance(value, basestring):
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