1*e1fe3e4aSElliott Hughes"""Various low level data validators.""" 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott Hughesimport calendar 4*e1fe3e4aSElliott Hughesfrom io import open 5*e1fe3e4aSElliott Hughesimport fs.base 6*e1fe3e4aSElliott Hughesimport fs.osfs 7*e1fe3e4aSElliott Hughes 8*e1fe3e4aSElliott Hughesfrom collections.abc import Mapping 9*e1fe3e4aSElliott Hughesfrom fontTools.ufoLib.utils import numberTypes 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughes# ------- 13*e1fe3e4aSElliott Hughes# Generic 14*e1fe3e4aSElliott Hughes# ------- 15*e1fe3e4aSElliott Hughes 16*e1fe3e4aSElliott Hughes 17*e1fe3e4aSElliott Hughesdef isDictEnough(value): 18*e1fe3e4aSElliott Hughes """ 19*e1fe3e4aSElliott Hughes Some objects will likely come in that aren't 20*e1fe3e4aSElliott Hughes dicts but are dict-ish enough. 21*e1fe3e4aSElliott Hughes """ 22*e1fe3e4aSElliott Hughes if isinstance(value, Mapping): 23*e1fe3e4aSElliott Hughes return True 24*e1fe3e4aSElliott Hughes for attr in ("keys", "values", "items"): 25*e1fe3e4aSElliott Hughes if not hasattr(value, attr): 26*e1fe3e4aSElliott Hughes return False 27*e1fe3e4aSElliott Hughes return True 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughesdef genericTypeValidator(value, typ): 31*e1fe3e4aSElliott Hughes """ 32*e1fe3e4aSElliott Hughes Generic. (Added at version 2.) 33*e1fe3e4aSElliott Hughes """ 34*e1fe3e4aSElliott Hughes return isinstance(value, typ) 35*e1fe3e4aSElliott Hughes 36*e1fe3e4aSElliott Hughes 37*e1fe3e4aSElliott Hughesdef genericIntListValidator(values, validValues): 38*e1fe3e4aSElliott Hughes """ 39*e1fe3e4aSElliott Hughes Generic. (Added at version 2.) 40*e1fe3e4aSElliott Hughes """ 41*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 42*e1fe3e4aSElliott Hughes return False 43*e1fe3e4aSElliott Hughes valuesSet = set(values) 44*e1fe3e4aSElliott Hughes validValuesSet = set(validValues) 45*e1fe3e4aSElliott Hughes if valuesSet - validValuesSet: 46*e1fe3e4aSElliott Hughes return False 47*e1fe3e4aSElliott Hughes for value in values: 48*e1fe3e4aSElliott Hughes if not isinstance(value, int): 49*e1fe3e4aSElliott Hughes return False 50*e1fe3e4aSElliott Hughes return True 51*e1fe3e4aSElliott Hughes 52*e1fe3e4aSElliott Hughes 53*e1fe3e4aSElliott Hughesdef genericNonNegativeIntValidator(value): 54*e1fe3e4aSElliott Hughes """ 55*e1fe3e4aSElliott Hughes Generic. (Added at version 3.) 56*e1fe3e4aSElliott Hughes """ 57*e1fe3e4aSElliott Hughes if not isinstance(value, int): 58*e1fe3e4aSElliott Hughes return False 59*e1fe3e4aSElliott Hughes if value < 0: 60*e1fe3e4aSElliott Hughes return False 61*e1fe3e4aSElliott Hughes return True 62*e1fe3e4aSElliott Hughes 63*e1fe3e4aSElliott Hughes 64*e1fe3e4aSElliott Hughesdef genericNonNegativeNumberValidator(value): 65*e1fe3e4aSElliott Hughes """ 66*e1fe3e4aSElliott Hughes Generic. (Added at version 3.) 67*e1fe3e4aSElliott Hughes """ 68*e1fe3e4aSElliott Hughes if not isinstance(value, numberTypes): 69*e1fe3e4aSElliott Hughes return False 70*e1fe3e4aSElliott Hughes if value < 0: 71*e1fe3e4aSElliott Hughes return False 72*e1fe3e4aSElliott Hughes return True 73*e1fe3e4aSElliott Hughes 74*e1fe3e4aSElliott Hughes 75*e1fe3e4aSElliott Hughesdef genericDictValidator(value, prototype): 76*e1fe3e4aSElliott Hughes """ 77*e1fe3e4aSElliott Hughes Generic. (Added at version 3.) 78*e1fe3e4aSElliott Hughes """ 79*e1fe3e4aSElliott Hughes # not a dict 80*e1fe3e4aSElliott Hughes if not isinstance(value, Mapping): 81*e1fe3e4aSElliott Hughes return False 82*e1fe3e4aSElliott Hughes # missing required keys 83*e1fe3e4aSElliott Hughes for key, (typ, required) in prototype.items(): 84*e1fe3e4aSElliott Hughes if not required: 85*e1fe3e4aSElliott Hughes continue 86*e1fe3e4aSElliott Hughes if key not in value: 87*e1fe3e4aSElliott Hughes return False 88*e1fe3e4aSElliott Hughes # unknown keys 89*e1fe3e4aSElliott Hughes for key in value.keys(): 90*e1fe3e4aSElliott Hughes if key not in prototype: 91*e1fe3e4aSElliott Hughes return False 92*e1fe3e4aSElliott Hughes # incorrect types 93*e1fe3e4aSElliott Hughes for key, v in value.items(): 94*e1fe3e4aSElliott Hughes prototypeType, required = prototype[key] 95*e1fe3e4aSElliott Hughes if v is None and not required: 96*e1fe3e4aSElliott Hughes continue 97*e1fe3e4aSElliott Hughes if not isinstance(v, prototypeType): 98*e1fe3e4aSElliott Hughes return False 99*e1fe3e4aSElliott Hughes return True 100*e1fe3e4aSElliott Hughes 101*e1fe3e4aSElliott Hughes 102*e1fe3e4aSElliott Hughes# -------------- 103*e1fe3e4aSElliott Hughes# fontinfo.plist 104*e1fe3e4aSElliott Hughes# -------------- 105*e1fe3e4aSElliott Hughes 106*e1fe3e4aSElliott Hughes# Data Validators 107*e1fe3e4aSElliott Hughes 108*e1fe3e4aSElliott Hughes 109*e1fe3e4aSElliott Hughesdef fontInfoStyleMapStyleNameValidator(value): 110*e1fe3e4aSElliott Hughes """ 111*e1fe3e4aSElliott Hughes Version 2+. 112*e1fe3e4aSElliott Hughes """ 113*e1fe3e4aSElliott Hughes options = ["regular", "italic", "bold", "bold italic"] 114*e1fe3e4aSElliott Hughes return value in options 115*e1fe3e4aSElliott Hughes 116*e1fe3e4aSElliott Hughes 117*e1fe3e4aSElliott Hughesdef fontInfoOpenTypeGaspRangeRecordsValidator(value): 118*e1fe3e4aSElliott Hughes """ 119*e1fe3e4aSElliott Hughes Version 3+. 120*e1fe3e4aSElliott Hughes """ 121*e1fe3e4aSElliott Hughes if not isinstance(value, list): 122*e1fe3e4aSElliott Hughes return False 123*e1fe3e4aSElliott Hughes if len(value) == 0: 124*e1fe3e4aSElliott Hughes return True 125*e1fe3e4aSElliott Hughes validBehaviors = [0, 1, 2, 3] 126*e1fe3e4aSElliott Hughes dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True)) 127*e1fe3e4aSElliott Hughes ppemOrder = [] 128*e1fe3e4aSElliott Hughes for rangeRecord in value: 129*e1fe3e4aSElliott Hughes if not genericDictValidator(rangeRecord, dictPrototype): 130*e1fe3e4aSElliott Hughes return False 131*e1fe3e4aSElliott Hughes ppem = rangeRecord["rangeMaxPPEM"] 132*e1fe3e4aSElliott Hughes behavior = rangeRecord["rangeGaspBehavior"] 133*e1fe3e4aSElliott Hughes ppemValidity = genericNonNegativeIntValidator(ppem) 134*e1fe3e4aSElliott Hughes if not ppemValidity: 135*e1fe3e4aSElliott Hughes return False 136*e1fe3e4aSElliott Hughes behaviorValidity = genericIntListValidator(behavior, validBehaviors) 137*e1fe3e4aSElliott Hughes if not behaviorValidity: 138*e1fe3e4aSElliott Hughes return False 139*e1fe3e4aSElliott Hughes ppemOrder.append(ppem) 140*e1fe3e4aSElliott Hughes if ppemOrder != sorted(ppemOrder): 141*e1fe3e4aSElliott Hughes return False 142*e1fe3e4aSElliott Hughes return True 143*e1fe3e4aSElliott Hughes 144*e1fe3e4aSElliott Hughes 145*e1fe3e4aSElliott Hughesdef fontInfoOpenTypeHeadCreatedValidator(value): 146*e1fe3e4aSElliott Hughes """ 147*e1fe3e4aSElliott Hughes Version 2+. 148*e1fe3e4aSElliott Hughes """ 149*e1fe3e4aSElliott Hughes # format: 0000/00/00 00:00:00 150*e1fe3e4aSElliott Hughes if not isinstance(value, str): 151*e1fe3e4aSElliott Hughes return False 152*e1fe3e4aSElliott Hughes # basic formatting 153*e1fe3e4aSElliott Hughes if not len(value) == 19: 154*e1fe3e4aSElliott Hughes return False 155*e1fe3e4aSElliott Hughes if value.count(" ") != 1: 156*e1fe3e4aSElliott Hughes return False 157*e1fe3e4aSElliott Hughes date, time = value.split(" ") 158*e1fe3e4aSElliott Hughes if date.count("/") != 2: 159*e1fe3e4aSElliott Hughes return False 160*e1fe3e4aSElliott Hughes if time.count(":") != 2: 161*e1fe3e4aSElliott Hughes return False 162*e1fe3e4aSElliott Hughes # date 163*e1fe3e4aSElliott Hughes year, month, day = date.split("/") 164*e1fe3e4aSElliott Hughes if len(year) != 4: 165*e1fe3e4aSElliott Hughes return False 166*e1fe3e4aSElliott Hughes if len(month) != 2: 167*e1fe3e4aSElliott Hughes return False 168*e1fe3e4aSElliott Hughes if len(day) != 2: 169*e1fe3e4aSElliott Hughes return False 170*e1fe3e4aSElliott Hughes try: 171*e1fe3e4aSElliott Hughes year = int(year) 172*e1fe3e4aSElliott Hughes month = int(month) 173*e1fe3e4aSElliott Hughes day = int(day) 174*e1fe3e4aSElliott Hughes except ValueError: 175*e1fe3e4aSElliott Hughes return False 176*e1fe3e4aSElliott Hughes if month < 1 or month > 12: 177*e1fe3e4aSElliott Hughes return False 178*e1fe3e4aSElliott Hughes monthMaxDay = calendar.monthrange(year, month)[1] 179*e1fe3e4aSElliott Hughes if day < 1 or day > monthMaxDay: 180*e1fe3e4aSElliott Hughes return False 181*e1fe3e4aSElliott Hughes # time 182*e1fe3e4aSElliott Hughes hour, minute, second = time.split(":") 183*e1fe3e4aSElliott Hughes if len(hour) != 2: 184*e1fe3e4aSElliott Hughes return False 185*e1fe3e4aSElliott Hughes if len(minute) != 2: 186*e1fe3e4aSElliott Hughes return False 187*e1fe3e4aSElliott Hughes if len(second) != 2: 188*e1fe3e4aSElliott Hughes return False 189*e1fe3e4aSElliott Hughes try: 190*e1fe3e4aSElliott Hughes hour = int(hour) 191*e1fe3e4aSElliott Hughes minute = int(minute) 192*e1fe3e4aSElliott Hughes second = int(second) 193*e1fe3e4aSElliott Hughes except ValueError: 194*e1fe3e4aSElliott Hughes return False 195*e1fe3e4aSElliott Hughes if hour < 0 or hour > 23: 196*e1fe3e4aSElliott Hughes return False 197*e1fe3e4aSElliott Hughes if minute < 0 or minute > 59: 198*e1fe3e4aSElliott Hughes return False 199*e1fe3e4aSElliott Hughes if second < 0 or second > 59: 200*e1fe3e4aSElliott Hughes return False 201*e1fe3e4aSElliott Hughes # fallback 202*e1fe3e4aSElliott Hughes return True 203*e1fe3e4aSElliott Hughes 204*e1fe3e4aSElliott Hughes 205*e1fe3e4aSElliott Hughesdef fontInfoOpenTypeNameRecordsValidator(value): 206*e1fe3e4aSElliott Hughes """ 207*e1fe3e4aSElliott Hughes Version 3+. 208*e1fe3e4aSElliott Hughes """ 209*e1fe3e4aSElliott Hughes if not isinstance(value, list): 210*e1fe3e4aSElliott Hughes return False 211*e1fe3e4aSElliott Hughes dictPrototype = dict( 212*e1fe3e4aSElliott Hughes nameID=(int, True), 213*e1fe3e4aSElliott Hughes platformID=(int, True), 214*e1fe3e4aSElliott Hughes encodingID=(int, True), 215*e1fe3e4aSElliott Hughes languageID=(int, True), 216*e1fe3e4aSElliott Hughes string=(str, True), 217*e1fe3e4aSElliott Hughes ) 218*e1fe3e4aSElliott Hughes for nameRecord in value: 219*e1fe3e4aSElliott Hughes if not genericDictValidator(nameRecord, dictPrototype): 220*e1fe3e4aSElliott Hughes return False 221*e1fe3e4aSElliott Hughes return True 222*e1fe3e4aSElliott Hughes 223*e1fe3e4aSElliott Hughes 224*e1fe3e4aSElliott Hughesdef fontInfoOpenTypeOS2WeightClassValidator(value): 225*e1fe3e4aSElliott Hughes """ 226*e1fe3e4aSElliott Hughes Version 2+. 227*e1fe3e4aSElliott Hughes """ 228*e1fe3e4aSElliott Hughes if not isinstance(value, int): 229*e1fe3e4aSElliott Hughes return False 230*e1fe3e4aSElliott Hughes if value < 0: 231*e1fe3e4aSElliott Hughes return False 232*e1fe3e4aSElliott Hughes return True 233*e1fe3e4aSElliott Hughes 234*e1fe3e4aSElliott Hughes 235*e1fe3e4aSElliott Hughesdef fontInfoOpenTypeOS2WidthClassValidator(value): 236*e1fe3e4aSElliott Hughes """ 237*e1fe3e4aSElliott Hughes Version 2+. 238*e1fe3e4aSElliott Hughes """ 239*e1fe3e4aSElliott Hughes if not isinstance(value, int): 240*e1fe3e4aSElliott Hughes return False 241*e1fe3e4aSElliott Hughes if value < 1: 242*e1fe3e4aSElliott Hughes return False 243*e1fe3e4aSElliott Hughes if value > 9: 244*e1fe3e4aSElliott Hughes return False 245*e1fe3e4aSElliott Hughes return True 246*e1fe3e4aSElliott Hughes 247*e1fe3e4aSElliott Hughes 248*e1fe3e4aSElliott Hughesdef fontInfoVersion2OpenTypeOS2PanoseValidator(values): 249*e1fe3e4aSElliott Hughes """ 250*e1fe3e4aSElliott Hughes Version 2. 251*e1fe3e4aSElliott Hughes """ 252*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 253*e1fe3e4aSElliott Hughes return False 254*e1fe3e4aSElliott Hughes if len(values) != 10: 255*e1fe3e4aSElliott Hughes return False 256*e1fe3e4aSElliott Hughes for value in values: 257*e1fe3e4aSElliott Hughes if not isinstance(value, int): 258*e1fe3e4aSElliott Hughes return False 259*e1fe3e4aSElliott Hughes # XXX further validation? 260*e1fe3e4aSElliott Hughes return True 261*e1fe3e4aSElliott Hughes 262*e1fe3e4aSElliott Hughes 263*e1fe3e4aSElliott Hughesdef fontInfoVersion3OpenTypeOS2PanoseValidator(values): 264*e1fe3e4aSElliott Hughes """ 265*e1fe3e4aSElliott Hughes Version 3+. 266*e1fe3e4aSElliott Hughes """ 267*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 268*e1fe3e4aSElliott Hughes return False 269*e1fe3e4aSElliott Hughes if len(values) != 10: 270*e1fe3e4aSElliott Hughes return False 271*e1fe3e4aSElliott Hughes for value in values: 272*e1fe3e4aSElliott Hughes if not isinstance(value, int): 273*e1fe3e4aSElliott Hughes return False 274*e1fe3e4aSElliott Hughes if value < 0: 275*e1fe3e4aSElliott Hughes return False 276*e1fe3e4aSElliott Hughes # XXX further validation? 277*e1fe3e4aSElliott Hughes return True 278*e1fe3e4aSElliott Hughes 279*e1fe3e4aSElliott Hughes 280*e1fe3e4aSElliott Hughesdef fontInfoOpenTypeOS2FamilyClassValidator(values): 281*e1fe3e4aSElliott Hughes """ 282*e1fe3e4aSElliott Hughes Version 2+. 283*e1fe3e4aSElliott Hughes """ 284*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 285*e1fe3e4aSElliott Hughes return False 286*e1fe3e4aSElliott Hughes if len(values) != 2: 287*e1fe3e4aSElliott Hughes return False 288*e1fe3e4aSElliott Hughes for value in values: 289*e1fe3e4aSElliott Hughes if not isinstance(value, int): 290*e1fe3e4aSElliott Hughes return False 291*e1fe3e4aSElliott Hughes classID, subclassID = values 292*e1fe3e4aSElliott Hughes if classID < 0 or classID > 14: 293*e1fe3e4aSElliott Hughes return False 294*e1fe3e4aSElliott Hughes if subclassID < 0 or subclassID > 15: 295*e1fe3e4aSElliott Hughes return False 296*e1fe3e4aSElliott Hughes return True 297*e1fe3e4aSElliott Hughes 298*e1fe3e4aSElliott Hughes 299*e1fe3e4aSElliott Hughesdef fontInfoPostscriptBluesValidator(values): 300*e1fe3e4aSElliott Hughes """ 301*e1fe3e4aSElliott Hughes Version 2+. 302*e1fe3e4aSElliott Hughes """ 303*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 304*e1fe3e4aSElliott Hughes return False 305*e1fe3e4aSElliott Hughes if len(values) > 14: 306*e1fe3e4aSElliott Hughes return False 307*e1fe3e4aSElliott Hughes if len(values) % 2: 308*e1fe3e4aSElliott Hughes return False 309*e1fe3e4aSElliott Hughes for value in values: 310*e1fe3e4aSElliott Hughes if not isinstance(value, numberTypes): 311*e1fe3e4aSElliott Hughes return False 312*e1fe3e4aSElliott Hughes return True 313*e1fe3e4aSElliott Hughes 314*e1fe3e4aSElliott Hughes 315*e1fe3e4aSElliott Hughesdef fontInfoPostscriptOtherBluesValidator(values): 316*e1fe3e4aSElliott Hughes """ 317*e1fe3e4aSElliott Hughes Version 2+. 318*e1fe3e4aSElliott Hughes """ 319*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 320*e1fe3e4aSElliott Hughes return False 321*e1fe3e4aSElliott Hughes if len(values) > 10: 322*e1fe3e4aSElliott Hughes return False 323*e1fe3e4aSElliott Hughes if len(values) % 2: 324*e1fe3e4aSElliott Hughes return False 325*e1fe3e4aSElliott Hughes for value in values: 326*e1fe3e4aSElliott Hughes if not isinstance(value, numberTypes): 327*e1fe3e4aSElliott Hughes return False 328*e1fe3e4aSElliott Hughes return True 329*e1fe3e4aSElliott Hughes 330*e1fe3e4aSElliott Hughes 331*e1fe3e4aSElliott Hughesdef fontInfoPostscriptStemsValidator(values): 332*e1fe3e4aSElliott Hughes """ 333*e1fe3e4aSElliott Hughes Version 2+. 334*e1fe3e4aSElliott Hughes """ 335*e1fe3e4aSElliott Hughes if not isinstance(values, (list, tuple)): 336*e1fe3e4aSElliott Hughes return False 337*e1fe3e4aSElliott Hughes if len(values) > 12: 338*e1fe3e4aSElliott Hughes return False 339*e1fe3e4aSElliott Hughes for value in values: 340*e1fe3e4aSElliott Hughes if not isinstance(value, numberTypes): 341*e1fe3e4aSElliott Hughes return False 342*e1fe3e4aSElliott Hughes return True 343*e1fe3e4aSElliott Hughes 344*e1fe3e4aSElliott Hughes 345*e1fe3e4aSElliott Hughesdef fontInfoPostscriptWindowsCharacterSetValidator(value): 346*e1fe3e4aSElliott Hughes """ 347*e1fe3e4aSElliott Hughes Version 2+. 348*e1fe3e4aSElliott Hughes """ 349*e1fe3e4aSElliott Hughes validValues = list(range(1, 21)) 350*e1fe3e4aSElliott Hughes if value not in validValues: 351*e1fe3e4aSElliott Hughes return False 352*e1fe3e4aSElliott Hughes return True 353*e1fe3e4aSElliott Hughes 354*e1fe3e4aSElliott Hughes 355*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataUniqueIDValidator(value): 356*e1fe3e4aSElliott Hughes """ 357*e1fe3e4aSElliott Hughes Version 3+. 358*e1fe3e4aSElliott Hughes """ 359*e1fe3e4aSElliott Hughes dictPrototype = dict(id=(str, True)) 360*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 361*e1fe3e4aSElliott Hughes return False 362*e1fe3e4aSElliott Hughes return True 363*e1fe3e4aSElliott Hughes 364*e1fe3e4aSElliott Hughes 365*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataVendorValidator(value): 366*e1fe3e4aSElliott Hughes """ 367*e1fe3e4aSElliott Hughes Version 3+. 368*e1fe3e4aSElliott Hughes """ 369*e1fe3e4aSElliott Hughes dictPrototype = { 370*e1fe3e4aSElliott Hughes "name": (str, True), 371*e1fe3e4aSElliott Hughes "url": (str, False), 372*e1fe3e4aSElliott Hughes "dir": (str, False), 373*e1fe3e4aSElliott Hughes "class": (str, False), 374*e1fe3e4aSElliott Hughes } 375*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 376*e1fe3e4aSElliott Hughes return False 377*e1fe3e4aSElliott Hughes if "dir" in value and value.get("dir") not in ("ltr", "rtl"): 378*e1fe3e4aSElliott Hughes return False 379*e1fe3e4aSElliott Hughes return True 380*e1fe3e4aSElliott Hughes 381*e1fe3e4aSElliott Hughes 382*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataCreditsValidator(value): 383*e1fe3e4aSElliott Hughes """ 384*e1fe3e4aSElliott Hughes Version 3+. 385*e1fe3e4aSElliott Hughes """ 386*e1fe3e4aSElliott Hughes dictPrototype = dict(credits=(list, True)) 387*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 388*e1fe3e4aSElliott Hughes return False 389*e1fe3e4aSElliott Hughes if not len(value["credits"]): 390*e1fe3e4aSElliott Hughes return False 391*e1fe3e4aSElliott Hughes dictPrototype = { 392*e1fe3e4aSElliott Hughes "name": (str, True), 393*e1fe3e4aSElliott Hughes "url": (str, False), 394*e1fe3e4aSElliott Hughes "role": (str, False), 395*e1fe3e4aSElliott Hughes "dir": (str, False), 396*e1fe3e4aSElliott Hughes "class": (str, False), 397*e1fe3e4aSElliott Hughes } 398*e1fe3e4aSElliott Hughes for credit in value["credits"]: 399*e1fe3e4aSElliott Hughes if not genericDictValidator(credit, dictPrototype): 400*e1fe3e4aSElliott Hughes return False 401*e1fe3e4aSElliott Hughes if "dir" in credit and credit.get("dir") not in ("ltr", "rtl"): 402*e1fe3e4aSElliott Hughes return False 403*e1fe3e4aSElliott Hughes return True 404*e1fe3e4aSElliott Hughes 405*e1fe3e4aSElliott Hughes 406*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataDescriptionValidator(value): 407*e1fe3e4aSElliott Hughes """ 408*e1fe3e4aSElliott Hughes Version 3+. 409*e1fe3e4aSElliott Hughes """ 410*e1fe3e4aSElliott Hughes dictPrototype = dict(url=(str, False), text=(list, True)) 411*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 412*e1fe3e4aSElliott Hughes return False 413*e1fe3e4aSElliott Hughes for text in value["text"]: 414*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataTextValue(text): 415*e1fe3e4aSElliott Hughes return False 416*e1fe3e4aSElliott Hughes return True 417*e1fe3e4aSElliott Hughes 418*e1fe3e4aSElliott Hughes 419*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataLicenseValidator(value): 420*e1fe3e4aSElliott Hughes """ 421*e1fe3e4aSElliott Hughes Version 3+. 422*e1fe3e4aSElliott Hughes """ 423*e1fe3e4aSElliott Hughes dictPrototype = dict(url=(str, False), text=(list, False), id=(str, False)) 424*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 425*e1fe3e4aSElliott Hughes return False 426*e1fe3e4aSElliott Hughes if "text" in value: 427*e1fe3e4aSElliott Hughes for text in value["text"]: 428*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataTextValue(text): 429*e1fe3e4aSElliott Hughes return False 430*e1fe3e4aSElliott Hughes return True 431*e1fe3e4aSElliott Hughes 432*e1fe3e4aSElliott Hughes 433*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataTrademarkValidator(value): 434*e1fe3e4aSElliott Hughes """ 435*e1fe3e4aSElliott Hughes Version 3+. 436*e1fe3e4aSElliott Hughes """ 437*e1fe3e4aSElliott Hughes dictPrototype = dict(text=(list, True)) 438*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 439*e1fe3e4aSElliott Hughes return False 440*e1fe3e4aSElliott Hughes for text in value["text"]: 441*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataTextValue(text): 442*e1fe3e4aSElliott Hughes return False 443*e1fe3e4aSElliott Hughes return True 444*e1fe3e4aSElliott Hughes 445*e1fe3e4aSElliott Hughes 446*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataCopyrightValidator(value): 447*e1fe3e4aSElliott Hughes """ 448*e1fe3e4aSElliott Hughes Version 3+. 449*e1fe3e4aSElliott Hughes """ 450*e1fe3e4aSElliott Hughes dictPrototype = dict(text=(list, True)) 451*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 452*e1fe3e4aSElliott Hughes return False 453*e1fe3e4aSElliott Hughes for text in value["text"]: 454*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataTextValue(text): 455*e1fe3e4aSElliott Hughes return False 456*e1fe3e4aSElliott Hughes return True 457*e1fe3e4aSElliott Hughes 458*e1fe3e4aSElliott Hughes 459*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataLicenseeValidator(value): 460*e1fe3e4aSElliott Hughes """ 461*e1fe3e4aSElliott Hughes Version 3+. 462*e1fe3e4aSElliott Hughes """ 463*e1fe3e4aSElliott Hughes dictPrototype = {"name": (str, True), "dir": (str, False), "class": (str, False)} 464*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 465*e1fe3e4aSElliott Hughes return False 466*e1fe3e4aSElliott Hughes if "dir" in value and value.get("dir") not in ("ltr", "rtl"): 467*e1fe3e4aSElliott Hughes return False 468*e1fe3e4aSElliott Hughes return True 469*e1fe3e4aSElliott Hughes 470*e1fe3e4aSElliott Hughes 471*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataTextValue(value): 472*e1fe3e4aSElliott Hughes """ 473*e1fe3e4aSElliott Hughes Version 3+. 474*e1fe3e4aSElliott Hughes """ 475*e1fe3e4aSElliott Hughes dictPrototype = { 476*e1fe3e4aSElliott Hughes "text": (str, True), 477*e1fe3e4aSElliott Hughes "language": (str, False), 478*e1fe3e4aSElliott Hughes "dir": (str, False), 479*e1fe3e4aSElliott Hughes "class": (str, False), 480*e1fe3e4aSElliott Hughes } 481*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 482*e1fe3e4aSElliott Hughes return False 483*e1fe3e4aSElliott Hughes if "dir" in value and value.get("dir") not in ("ltr", "rtl"): 484*e1fe3e4aSElliott Hughes return False 485*e1fe3e4aSElliott Hughes return True 486*e1fe3e4aSElliott Hughes 487*e1fe3e4aSElliott Hughes 488*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataExtensionsValidator(value): 489*e1fe3e4aSElliott Hughes """ 490*e1fe3e4aSElliott Hughes Version 3+. 491*e1fe3e4aSElliott Hughes """ 492*e1fe3e4aSElliott Hughes if not isinstance(value, list): 493*e1fe3e4aSElliott Hughes return False 494*e1fe3e4aSElliott Hughes if not value: 495*e1fe3e4aSElliott Hughes return False 496*e1fe3e4aSElliott Hughes for extension in value: 497*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataExtensionValidator(extension): 498*e1fe3e4aSElliott Hughes return False 499*e1fe3e4aSElliott Hughes return True 500*e1fe3e4aSElliott Hughes 501*e1fe3e4aSElliott Hughes 502*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataExtensionValidator(value): 503*e1fe3e4aSElliott Hughes """ 504*e1fe3e4aSElliott Hughes Version 3+. 505*e1fe3e4aSElliott Hughes """ 506*e1fe3e4aSElliott Hughes dictPrototype = dict(names=(list, False), items=(list, True), id=(str, False)) 507*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 508*e1fe3e4aSElliott Hughes return False 509*e1fe3e4aSElliott Hughes if "names" in value: 510*e1fe3e4aSElliott Hughes for name in value["names"]: 511*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataExtensionNameValidator(name): 512*e1fe3e4aSElliott Hughes return False 513*e1fe3e4aSElliott Hughes for item in value["items"]: 514*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataExtensionItemValidator(item): 515*e1fe3e4aSElliott Hughes return False 516*e1fe3e4aSElliott Hughes return True 517*e1fe3e4aSElliott Hughes 518*e1fe3e4aSElliott Hughes 519*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataExtensionItemValidator(value): 520*e1fe3e4aSElliott Hughes """ 521*e1fe3e4aSElliott Hughes Version 3+. 522*e1fe3e4aSElliott Hughes """ 523*e1fe3e4aSElliott Hughes dictPrototype = dict(id=(str, False), names=(list, True), values=(list, True)) 524*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 525*e1fe3e4aSElliott Hughes return False 526*e1fe3e4aSElliott Hughes for name in value["names"]: 527*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataExtensionNameValidator(name): 528*e1fe3e4aSElliott Hughes return False 529*e1fe3e4aSElliott Hughes for val in value["values"]: 530*e1fe3e4aSElliott Hughes if not fontInfoWOFFMetadataExtensionValueValidator(val): 531*e1fe3e4aSElliott Hughes return False 532*e1fe3e4aSElliott Hughes return True 533*e1fe3e4aSElliott Hughes 534*e1fe3e4aSElliott Hughes 535*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataExtensionNameValidator(value): 536*e1fe3e4aSElliott Hughes """ 537*e1fe3e4aSElliott Hughes Version 3+. 538*e1fe3e4aSElliott Hughes """ 539*e1fe3e4aSElliott Hughes dictPrototype = { 540*e1fe3e4aSElliott Hughes "text": (str, True), 541*e1fe3e4aSElliott Hughes "language": (str, False), 542*e1fe3e4aSElliott Hughes "dir": (str, False), 543*e1fe3e4aSElliott Hughes "class": (str, False), 544*e1fe3e4aSElliott Hughes } 545*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 546*e1fe3e4aSElliott Hughes return False 547*e1fe3e4aSElliott Hughes if "dir" in value and value.get("dir") not in ("ltr", "rtl"): 548*e1fe3e4aSElliott Hughes return False 549*e1fe3e4aSElliott Hughes return True 550*e1fe3e4aSElliott Hughes 551*e1fe3e4aSElliott Hughes 552*e1fe3e4aSElliott Hughesdef fontInfoWOFFMetadataExtensionValueValidator(value): 553*e1fe3e4aSElliott Hughes """ 554*e1fe3e4aSElliott Hughes Version 3+. 555*e1fe3e4aSElliott Hughes """ 556*e1fe3e4aSElliott Hughes dictPrototype = { 557*e1fe3e4aSElliott Hughes "text": (str, True), 558*e1fe3e4aSElliott Hughes "language": (str, False), 559*e1fe3e4aSElliott Hughes "dir": (str, False), 560*e1fe3e4aSElliott Hughes "class": (str, False), 561*e1fe3e4aSElliott Hughes } 562*e1fe3e4aSElliott Hughes if not genericDictValidator(value, dictPrototype): 563*e1fe3e4aSElliott Hughes return False 564*e1fe3e4aSElliott Hughes if "dir" in value and value.get("dir") not in ("ltr", "rtl"): 565*e1fe3e4aSElliott Hughes return False 566*e1fe3e4aSElliott Hughes return True 567*e1fe3e4aSElliott Hughes 568*e1fe3e4aSElliott Hughes 569*e1fe3e4aSElliott Hughes# ---------- 570*e1fe3e4aSElliott Hughes# Guidelines 571*e1fe3e4aSElliott Hughes# ---------- 572*e1fe3e4aSElliott Hughes 573*e1fe3e4aSElliott Hughes 574*e1fe3e4aSElliott Hughesdef guidelinesValidator(value, identifiers=None): 575*e1fe3e4aSElliott Hughes """ 576*e1fe3e4aSElliott Hughes Version 3+. 577*e1fe3e4aSElliott Hughes """ 578*e1fe3e4aSElliott Hughes if not isinstance(value, list): 579*e1fe3e4aSElliott Hughes return False 580*e1fe3e4aSElliott Hughes if identifiers is None: 581*e1fe3e4aSElliott Hughes identifiers = set() 582*e1fe3e4aSElliott Hughes for guide in value: 583*e1fe3e4aSElliott Hughes if not guidelineValidator(guide): 584*e1fe3e4aSElliott Hughes return False 585*e1fe3e4aSElliott Hughes identifier = guide.get("identifier") 586*e1fe3e4aSElliott Hughes if identifier is not None: 587*e1fe3e4aSElliott Hughes if identifier in identifiers: 588*e1fe3e4aSElliott Hughes return False 589*e1fe3e4aSElliott Hughes identifiers.add(identifier) 590*e1fe3e4aSElliott Hughes return True 591*e1fe3e4aSElliott Hughes 592*e1fe3e4aSElliott Hughes 593*e1fe3e4aSElliott Hughes_guidelineDictPrototype = dict( 594*e1fe3e4aSElliott Hughes x=((int, float), False), 595*e1fe3e4aSElliott Hughes y=((int, float), False), 596*e1fe3e4aSElliott Hughes angle=((int, float), False), 597*e1fe3e4aSElliott Hughes name=(str, False), 598*e1fe3e4aSElliott Hughes color=(str, False), 599*e1fe3e4aSElliott Hughes identifier=(str, False), 600*e1fe3e4aSElliott Hughes) 601*e1fe3e4aSElliott Hughes 602*e1fe3e4aSElliott Hughes 603*e1fe3e4aSElliott Hughesdef guidelineValidator(value): 604*e1fe3e4aSElliott Hughes """ 605*e1fe3e4aSElliott Hughes Version 3+. 606*e1fe3e4aSElliott Hughes """ 607*e1fe3e4aSElliott Hughes if not genericDictValidator(value, _guidelineDictPrototype): 608*e1fe3e4aSElliott Hughes return False 609*e1fe3e4aSElliott Hughes x = value.get("x") 610*e1fe3e4aSElliott Hughes y = value.get("y") 611*e1fe3e4aSElliott Hughes angle = value.get("angle") 612*e1fe3e4aSElliott Hughes # x or y must be present 613*e1fe3e4aSElliott Hughes if x is None and y is None: 614*e1fe3e4aSElliott Hughes return False 615*e1fe3e4aSElliott Hughes # if x or y are None, angle must not be present 616*e1fe3e4aSElliott Hughes if x is None or y is None: 617*e1fe3e4aSElliott Hughes if angle is not None: 618*e1fe3e4aSElliott Hughes return False 619*e1fe3e4aSElliott Hughes # if x and y are defined, angle must be defined 620*e1fe3e4aSElliott Hughes if x is not None and y is not None and angle is None: 621*e1fe3e4aSElliott Hughes return False 622*e1fe3e4aSElliott Hughes # angle must be between 0 and 360 623*e1fe3e4aSElliott Hughes if angle is not None: 624*e1fe3e4aSElliott Hughes if angle < 0: 625*e1fe3e4aSElliott Hughes return False 626*e1fe3e4aSElliott Hughes if angle > 360: 627*e1fe3e4aSElliott Hughes return False 628*e1fe3e4aSElliott Hughes # identifier must be 1 or more characters 629*e1fe3e4aSElliott Hughes identifier = value.get("identifier") 630*e1fe3e4aSElliott Hughes if identifier is not None and not identifierValidator(identifier): 631*e1fe3e4aSElliott Hughes return False 632*e1fe3e4aSElliott Hughes # color must follow the proper format 633*e1fe3e4aSElliott Hughes color = value.get("color") 634*e1fe3e4aSElliott Hughes if color is not None and not colorValidator(color): 635*e1fe3e4aSElliott Hughes return False 636*e1fe3e4aSElliott Hughes return True 637*e1fe3e4aSElliott Hughes 638*e1fe3e4aSElliott Hughes 639*e1fe3e4aSElliott Hughes# ------- 640*e1fe3e4aSElliott Hughes# Anchors 641*e1fe3e4aSElliott Hughes# ------- 642*e1fe3e4aSElliott Hughes 643*e1fe3e4aSElliott Hughes 644*e1fe3e4aSElliott Hughesdef anchorsValidator(value, identifiers=None): 645*e1fe3e4aSElliott Hughes """ 646*e1fe3e4aSElliott Hughes Version 3+. 647*e1fe3e4aSElliott Hughes """ 648*e1fe3e4aSElliott Hughes if not isinstance(value, list): 649*e1fe3e4aSElliott Hughes return False 650*e1fe3e4aSElliott Hughes if identifiers is None: 651*e1fe3e4aSElliott Hughes identifiers = set() 652*e1fe3e4aSElliott Hughes for anchor in value: 653*e1fe3e4aSElliott Hughes if not anchorValidator(anchor): 654*e1fe3e4aSElliott Hughes return False 655*e1fe3e4aSElliott Hughes identifier = anchor.get("identifier") 656*e1fe3e4aSElliott Hughes if identifier is not None: 657*e1fe3e4aSElliott Hughes if identifier in identifiers: 658*e1fe3e4aSElliott Hughes return False 659*e1fe3e4aSElliott Hughes identifiers.add(identifier) 660*e1fe3e4aSElliott Hughes return True 661*e1fe3e4aSElliott Hughes 662*e1fe3e4aSElliott Hughes 663*e1fe3e4aSElliott Hughes_anchorDictPrototype = dict( 664*e1fe3e4aSElliott Hughes x=((int, float), False), 665*e1fe3e4aSElliott Hughes y=((int, float), False), 666*e1fe3e4aSElliott Hughes name=(str, False), 667*e1fe3e4aSElliott Hughes color=(str, False), 668*e1fe3e4aSElliott Hughes identifier=(str, False), 669*e1fe3e4aSElliott Hughes) 670*e1fe3e4aSElliott Hughes 671*e1fe3e4aSElliott Hughes 672*e1fe3e4aSElliott Hughesdef anchorValidator(value): 673*e1fe3e4aSElliott Hughes """ 674*e1fe3e4aSElliott Hughes Version 3+. 675*e1fe3e4aSElliott Hughes """ 676*e1fe3e4aSElliott Hughes if not genericDictValidator(value, _anchorDictPrototype): 677*e1fe3e4aSElliott Hughes return False 678*e1fe3e4aSElliott Hughes x = value.get("x") 679*e1fe3e4aSElliott Hughes y = value.get("y") 680*e1fe3e4aSElliott Hughes # x and y must be present 681*e1fe3e4aSElliott Hughes if x is None or y is None: 682*e1fe3e4aSElliott Hughes return False 683*e1fe3e4aSElliott Hughes # identifier must be 1 or more characters 684*e1fe3e4aSElliott Hughes identifier = value.get("identifier") 685*e1fe3e4aSElliott Hughes if identifier is not None and not identifierValidator(identifier): 686*e1fe3e4aSElliott Hughes return False 687*e1fe3e4aSElliott Hughes # color must follow the proper format 688*e1fe3e4aSElliott Hughes color = value.get("color") 689*e1fe3e4aSElliott Hughes if color is not None and not colorValidator(color): 690*e1fe3e4aSElliott Hughes return False 691*e1fe3e4aSElliott Hughes return True 692*e1fe3e4aSElliott Hughes 693*e1fe3e4aSElliott Hughes 694*e1fe3e4aSElliott Hughes# ---------- 695*e1fe3e4aSElliott Hughes# Identifier 696*e1fe3e4aSElliott Hughes# ---------- 697*e1fe3e4aSElliott Hughes 698*e1fe3e4aSElliott Hughes 699*e1fe3e4aSElliott Hughesdef identifierValidator(value): 700*e1fe3e4aSElliott Hughes """ 701*e1fe3e4aSElliott Hughes Version 3+. 702*e1fe3e4aSElliott Hughes 703*e1fe3e4aSElliott Hughes >>> identifierValidator("a") 704*e1fe3e4aSElliott Hughes True 705*e1fe3e4aSElliott Hughes >>> identifierValidator("") 706*e1fe3e4aSElliott Hughes False 707*e1fe3e4aSElliott Hughes >>> identifierValidator("a" * 101) 708*e1fe3e4aSElliott Hughes False 709*e1fe3e4aSElliott Hughes """ 710*e1fe3e4aSElliott Hughes validCharactersMin = 0x20 711*e1fe3e4aSElliott Hughes validCharactersMax = 0x7E 712*e1fe3e4aSElliott Hughes if not isinstance(value, str): 713*e1fe3e4aSElliott Hughes return False 714*e1fe3e4aSElliott Hughes if not value: 715*e1fe3e4aSElliott Hughes return False 716*e1fe3e4aSElliott Hughes if len(value) > 100: 717*e1fe3e4aSElliott Hughes return False 718*e1fe3e4aSElliott Hughes for c in value: 719*e1fe3e4aSElliott Hughes c = ord(c) 720*e1fe3e4aSElliott Hughes if c < validCharactersMin or c > validCharactersMax: 721*e1fe3e4aSElliott Hughes return False 722*e1fe3e4aSElliott Hughes return True 723*e1fe3e4aSElliott Hughes 724*e1fe3e4aSElliott Hughes 725*e1fe3e4aSElliott Hughes# ----- 726*e1fe3e4aSElliott Hughes# Color 727*e1fe3e4aSElliott Hughes# ----- 728*e1fe3e4aSElliott Hughes 729*e1fe3e4aSElliott Hughes 730*e1fe3e4aSElliott Hughesdef colorValidator(value): 731*e1fe3e4aSElliott Hughes """ 732*e1fe3e4aSElliott Hughes Version 3+. 733*e1fe3e4aSElliott Hughes 734*e1fe3e4aSElliott Hughes >>> colorValidator("0,0,0,0") 735*e1fe3e4aSElliott Hughes True 736*e1fe3e4aSElliott Hughes >>> colorValidator(".5,.5,.5,.5") 737*e1fe3e4aSElliott Hughes True 738*e1fe3e4aSElliott Hughes >>> colorValidator("0.5,0.5,0.5,0.5") 739*e1fe3e4aSElliott Hughes True 740*e1fe3e4aSElliott Hughes >>> colorValidator("1,1,1,1") 741*e1fe3e4aSElliott Hughes True 742*e1fe3e4aSElliott Hughes 743*e1fe3e4aSElliott Hughes >>> colorValidator("2,0,0,0") 744*e1fe3e4aSElliott Hughes False 745*e1fe3e4aSElliott Hughes >>> colorValidator("0,2,0,0") 746*e1fe3e4aSElliott Hughes False 747*e1fe3e4aSElliott Hughes >>> colorValidator("0,0,2,0") 748*e1fe3e4aSElliott Hughes False 749*e1fe3e4aSElliott Hughes >>> colorValidator("0,0,0,2") 750*e1fe3e4aSElliott Hughes False 751*e1fe3e4aSElliott Hughes 752*e1fe3e4aSElliott Hughes >>> colorValidator("1r,1,1,1") 753*e1fe3e4aSElliott Hughes False 754*e1fe3e4aSElliott Hughes >>> colorValidator("1,1g,1,1") 755*e1fe3e4aSElliott Hughes False 756*e1fe3e4aSElliott Hughes >>> colorValidator("1,1,1b,1") 757*e1fe3e4aSElliott Hughes False 758*e1fe3e4aSElliott Hughes >>> colorValidator("1,1,1,1a") 759*e1fe3e4aSElliott Hughes False 760*e1fe3e4aSElliott Hughes 761*e1fe3e4aSElliott Hughes >>> colorValidator("1 1 1 1") 762*e1fe3e4aSElliott Hughes False 763*e1fe3e4aSElliott Hughes >>> colorValidator("1 1,1,1") 764*e1fe3e4aSElliott Hughes False 765*e1fe3e4aSElliott Hughes >>> colorValidator("1,1 1,1") 766*e1fe3e4aSElliott Hughes False 767*e1fe3e4aSElliott Hughes >>> colorValidator("1,1,1 1") 768*e1fe3e4aSElliott Hughes False 769*e1fe3e4aSElliott Hughes 770*e1fe3e4aSElliott Hughes >>> colorValidator("1, 1, 1, 1") 771*e1fe3e4aSElliott Hughes True 772*e1fe3e4aSElliott Hughes """ 773*e1fe3e4aSElliott Hughes if not isinstance(value, str): 774*e1fe3e4aSElliott Hughes return False 775*e1fe3e4aSElliott Hughes parts = value.split(",") 776*e1fe3e4aSElliott Hughes if len(parts) != 4: 777*e1fe3e4aSElliott Hughes return False 778*e1fe3e4aSElliott Hughes for part in parts: 779*e1fe3e4aSElliott Hughes part = part.strip() 780*e1fe3e4aSElliott Hughes converted = False 781*e1fe3e4aSElliott Hughes try: 782*e1fe3e4aSElliott Hughes part = int(part) 783*e1fe3e4aSElliott Hughes converted = True 784*e1fe3e4aSElliott Hughes except ValueError: 785*e1fe3e4aSElliott Hughes pass 786*e1fe3e4aSElliott Hughes if not converted: 787*e1fe3e4aSElliott Hughes try: 788*e1fe3e4aSElliott Hughes part = float(part) 789*e1fe3e4aSElliott Hughes converted = True 790*e1fe3e4aSElliott Hughes except ValueError: 791*e1fe3e4aSElliott Hughes pass 792*e1fe3e4aSElliott Hughes if not converted: 793*e1fe3e4aSElliott Hughes return False 794*e1fe3e4aSElliott Hughes if part < 0: 795*e1fe3e4aSElliott Hughes return False 796*e1fe3e4aSElliott Hughes if part > 1: 797*e1fe3e4aSElliott Hughes return False 798*e1fe3e4aSElliott Hughes return True 799*e1fe3e4aSElliott Hughes 800*e1fe3e4aSElliott Hughes 801*e1fe3e4aSElliott Hughes# ----- 802*e1fe3e4aSElliott Hughes# image 803*e1fe3e4aSElliott Hughes# ----- 804*e1fe3e4aSElliott Hughes 805*e1fe3e4aSElliott HughespngSignature = b"\x89PNG\r\n\x1a\n" 806*e1fe3e4aSElliott Hughes 807*e1fe3e4aSElliott Hughes_imageDictPrototype = dict( 808*e1fe3e4aSElliott Hughes fileName=(str, True), 809*e1fe3e4aSElliott Hughes xScale=((int, float), False), 810*e1fe3e4aSElliott Hughes xyScale=((int, float), False), 811*e1fe3e4aSElliott Hughes yxScale=((int, float), False), 812*e1fe3e4aSElliott Hughes yScale=((int, float), False), 813*e1fe3e4aSElliott Hughes xOffset=((int, float), False), 814*e1fe3e4aSElliott Hughes yOffset=((int, float), False), 815*e1fe3e4aSElliott Hughes color=(str, False), 816*e1fe3e4aSElliott Hughes) 817*e1fe3e4aSElliott Hughes 818*e1fe3e4aSElliott Hughes 819*e1fe3e4aSElliott Hughesdef imageValidator(value): 820*e1fe3e4aSElliott Hughes """ 821*e1fe3e4aSElliott Hughes Version 3+. 822*e1fe3e4aSElliott Hughes """ 823*e1fe3e4aSElliott Hughes if not genericDictValidator(value, _imageDictPrototype): 824*e1fe3e4aSElliott Hughes return False 825*e1fe3e4aSElliott Hughes # fileName must be one or more characters 826*e1fe3e4aSElliott Hughes if not value["fileName"]: 827*e1fe3e4aSElliott Hughes return False 828*e1fe3e4aSElliott Hughes # color must follow the proper format 829*e1fe3e4aSElliott Hughes color = value.get("color") 830*e1fe3e4aSElliott Hughes if color is not None and not colorValidator(color): 831*e1fe3e4aSElliott Hughes return False 832*e1fe3e4aSElliott Hughes return True 833*e1fe3e4aSElliott Hughes 834*e1fe3e4aSElliott Hughes 835*e1fe3e4aSElliott Hughesdef pngValidator(path=None, data=None, fileObj=None): 836*e1fe3e4aSElliott Hughes """ 837*e1fe3e4aSElliott Hughes Version 3+. 838*e1fe3e4aSElliott Hughes 839*e1fe3e4aSElliott Hughes This checks the signature of the image data. 840*e1fe3e4aSElliott Hughes """ 841*e1fe3e4aSElliott Hughes assert path is not None or data is not None or fileObj is not None 842*e1fe3e4aSElliott Hughes if path is not None: 843*e1fe3e4aSElliott Hughes with open(path, "rb") as f: 844*e1fe3e4aSElliott Hughes signature = f.read(8) 845*e1fe3e4aSElliott Hughes elif data is not None: 846*e1fe3e4aSElliott Hughes signature = data[:8] 847*e1fe3e4aSElliott Hughes elif fileObj is not None: 848*e1fe3e4aSElliott Hughes pos = fileObj.tell() 849*e1fe3e4aSElliott Hughes signature = fileObj.read(8) 850*e1fe3e4aSElliott Hughes fileObj.seek(pos) 851*e1fe3e4aSElliott Hughes if signature != pngSignature: 852*e1fe3e4aSElliott Hughes return False, "Image does not begin with the PNG signature." 853*e1fe3e4aSElliott Hughes return True, None 854*e1fe3e4aSElliott Hughes 855*e1fe3e4aSElliott Hughes 856*e1fe3e4aSElliott Hughes# ------------------- 857*e1fe3e4aSElliott Hughes# layercontents.plist 858*e1fe3e4aSElliott Hughes# ------------------- 859*e1fe3e4aSElliott Hughes 860*e1fe3e4aSElliott Hughes 861*e1fe3e4aSElliott Hughesdef layerContentsValidator(value, ufoPathOrFileSystem): 862*e1fe3e4aSElliott Hughes """ 863*e1fe3e4aSElliott Hughes Check the validity of layercontents.plist. 864*e1fe3e4aSElliott Hughes Version 3+. 865*e1fe3e4aSElliott Hughes """ 866*e1fe3e4aSElliott Hughes if isinstance(ufoPathOrFileSystem, fs.base.FS): 867*e1fe3e4aSElliott Hughes fileSystem = ufoPathOrFileSystem 868*e1fe3e4aSElliott Hughes else: 869*e1fe3e4aSElliott Hughes fileSystem = fs.osfs.OSFS(ufoPathOrFileSystem) 870*e1fe3e4aSElliott Hughes 871*e1fe3e4aSElliott Hughes bogusFileMessage = "layercontents.plist in not in the correct format." 872*e1fe3e4aSElliott Hughes # file isn't in the right format 873*e1fe3e4aSElliott Hughes if not isinstance(value, list): 874*e1fe3e4aSElliott Hughes return False, bogusFileMessage 875*e1fe3e4aSElliott Hughes # work through each entry 876*e1fe3e4aSElliott Hughes usedLayerNames = set() 877*e1fe3e4aSElliott Hughes usedDirectories = set() 878*e1fe3e4aSElliott Hughes contents = {} 879*e1fe3e4aSElliott Hughes for entry in value: 880*e1fe3e4aSElliott Hughes # layer entry in the incorrect format 881*e1fe3e4aSElliott Hughes if not isinstance(entry, list): 882*e1fe3e4aSElliott Hughes return False, bogusFileMessage 883*e1fe3e4aSElliott Hughes if not len(entry) == 2: 884*e1fe3e4aSElliott Hughes return False, bogusFileMessage 885*e1fe3e4aSElliott Hughes for i in entry: 886*e1fe3e4aSElliott Hughes if not isinstance(i, str): 887*e1fe3e4aSElliott Hughes return False, bogusFileMessage 888*e1fe3e4aSElliott Hughes layerName, directoryName = entry 889*e1fe3e4aSElliott Hughes # check directory naming 890*e1fe3e4aSElliott Hughes if directoryName != "glyphs": 891*e1fe3e4aSElliott Hughes if not directoryName.startswith("glyphs."): 892*e1fe3e4aSElliott Hughes return ( 893*e1fe3e4aSElliott Hughes False, 894*e1fe3e4aSElliott Hughes "Invalid directory name (%s) in layercontents.plist." 895*e1fe3e4aSElliott Hughes % directoryName, 896*e1fe3e4aSElliott Hughes ) 897*e1fe3e4aSElliott Hughes if len(layerName) == 0: 898*e1fe3e4aSElliott Hughes return False, "Empty layer name in layercontents.plist." 899*e1fe3e4aSElliott Hughes # directory doesn't exist 900*e1fe3e4aSElliott Hughes if not fileSystem.exists(directoryName): 901*e1fe3e4aSElliott Hughes return False, "A glyphset does not exist at %s." % directoryName 902*e1fe3e4aSElliott Hughes # default layer name 903*e1fe3e4aSElliott Hughes if layerName == "public.default" and directoryName != "glyphs": 904*e1fe3e4aSElliott Hughes return ( 905*e1fe3e4aSElliott Hughes False, 906*e1fe3e4aSElliott Hughes "The name public.default is being used by a layer that is not the default.", 907*e1fe3e4aSElliott Hughes ) 908*e1fe3e4aSElliott Hughes # check usage 909*e1fe3e4aSElliott Hughes if layerName in usedLayerNames: 910*e1fe3e4aSElliott Hughes return ( 911*e1fe3e4aSElliott Hughes False, 912*e1fe3e4aSElliott Hughes "The layer name %s is used by more than one layer." % layerName, 913*e1fe3e4aSElliott Hughes ) 914*e1fe3e4aSElliott Hughes usedLayerNames.add(layerName) 915*e1fe3e4aSElliott Hughes if directoryName in usedDirectories: 916*e1fe3e4aSElliott Hughes return ( 917*e1fe3e4aSElliott Hughes False, 918*e1fe3e4aSElliott Hughes "The directory %s is used by more than one layer." % directoryName, 919*e1fe3e4aSElliott Hughes ) 920*e1fe3e4aSElliott Hughes usedDirectories.add(directoryName) 921*e1fe3e4aSElliott Hughes # store 922*e1fe3e4aSElliott Hughes contents[layerName] = directoryName 923*e1fe3e4aSElliott Hughes # missing default layer 924*e1fe3e4aSElliott Hughes foundDefault = "glyphs" in contents.values() 925*e1fe3e4aSElliott Hughes if not foundDefault: 926*e1fe3e4aSElliott Hughes return False, "The required default glyph set is not in the UFO." 927*e1fe3e4aSElliott Hughes return True, None 928*e1fe3e4aSElliott Hughes 929*e1fe3e4aSElliott Hughes 930*e1fe3e4aSElliott Hughes# ------------ 931*e1fe3e4aSElliott Hughes# groups.plist 932*e1fe3e4aSElliott Hughes# ------------ 933*e1fe3e4aSElliott Hughes 934*e1fe3e4aSElliott Hughes 935*e1fe3e4aSElliott Hughesdef groupsValidator(value): 936*e1fe3e4aSElliott Hughes """ 937*e1fe3e4aSElliott Hughes Check the validity of the groups. 938*e1fe3e4aSElliott Hughes Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). 939*e1fe3e4aSElliott Hughes 940*e1fe3e4aSElliott Hughes >>> groups = {"A" : ["A", "A"], "A2" : ["A"]} 941*e1fe3e4aSElliott Hughes >>> groupsValidator(groups) 942*e1fe3e4aSElliott Hughes (True, None) 943*e1fe3e4aSElliott Hughes 944*e1fe3e4aSElliott Hughes >>> groups = {"" : ["A"]} 945*e1fe3e4aSElliott Hughes >>> valid, msg = groupsValidator(groups) 946*e1fe3e4aSElliott Hughes >>> valid 947*e1fe3e4aSElliott Hughes False 948*e1fe3e4aSElliott Hughes >>> print(msg) 949*e1fe3e4aSElliott Hughes A group has an empty name. 950*e1fe3e4aSElliott Hughes 951*e1fe3e4aSElliott Hughes >>> groups = {"public.awesome" : ["A"]} 952*e1fe3e4aSElliott Hughes >>> groupsValidator(groups) 953*e1fe3e4aSElliott Hughes (True, None) 954*e1fe3e4aSElliott Hughes 955*e1fe3e4aSElliott Hughes >>> groups = {"public.kern1." : ["A"]} 956*e1fe3e4aSElliott Hughes >>> valid, msg = groupsValidator(groups) 957*e1fe3e4aSElliott Hughes >>> valid 958*e1fe3e4aSElliott Hughes False 959*e1fe3e4aSElliott Hughes >>> print(msg) 960*e1fe3e4aSElliott Hughes The group data contains a kerning group with an incomplete name. 961*e1fe3e4aSElliott Hughes >>> groups = {"public.kern2." : ["A"]} 962*e1fe3e4aSElliott Hughes >>> valid, msg = groupsValidator(groups) 963*e1fe3e4aSElliott Hughes >>> valid 964*e1fe3e4aSElliott Hughes False 965*e1fe3e4aSElliott Hughes >>> print(msg) 966*e1fe3e4aSElliott Hughes The group data contains a kerning group with an incomplete name. 967*e1fe3e4aSElliott Hughes 968*e1fe3e4aSElliott Hughes >>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]} 969*e1fe3e4aSElliott Hughes >>> groupsValidator(groups) 970*e1fe3e4aSElliott Hughes (True, None) 971*e1fe3e4aSElliott Hughes 972*e1fe3e4aSElliott Hughes >>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]} 973*e1fe3e4aSElliott Hughes >>> valid, msg = groupsValidator(groups) 974*e1fe3e4aSElliott Hughes >>> valid 975*e1fe3e4aSElliott Hughes False 976*e1fe3e4aSElliott Hughes >>> print(msg) 977*e1fe3e4aSElliott Hughes The glyph "A" occurs in too many kerning groups. 978*e1fe3e4aSElliott Hughes """ 979*e1fe3e4aSElliott Hughes bogusFormatMessage = "The group data is not in the correct format." 980*e1fe3e4aSElliott Hughes if not isDictEnough(value): 981*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 982*e1fe3e4aSElliott Hughes firstSideMapping = {} 983*e1fe3e4aSElliott Hughes secondSideMapping = {} 984*e1fe3e4aSElliott Hughes for groupName, glyphList in value.items(): 985*e1fe3e4aSElliott Hughes if not isinstance(groupName, (str)): 986*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 987*e1fe3e4aSElliott Hughes if not isinstance(glyphList, (list, tuple)): 988*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 989*e1fe3e4aSElliott Hughes if not groupName: 990*e1fe3e4aSElliott Hughes return False, "A group has an empty name." 991*e1fe3e4aSElliott Hughes if groupName.startswith("public."): 992*e1fe3e4aSElliott Hughes if not groupName.startswith("public.kern1.") and not groupName.startswith( 993*e1fe3e4aSElliott Hughes "public.kern2." 994*e1fe3e4aSElliott Hughes ): 995*e1fe3e4aSElliott Hughes # unknown public.* name. silently skip. 996*e1fe3e4aSElliott Hughes continue 997*e1fe3e4aSElliott Hughes else: 998*e1fe3e4aSElliott Hughes if len("public.kernN.") == len(groupName): 999*e1fe3e4aSElliott Hughes return ( 1000*e1fe3e4aSElliott Hughes False, 1001*e1fe3e4aSElliott Hughes "The group data contains a kerning group with an incomplete name.", 1002*e1fe3e4aSElliott Hughes ) 1003*e1fe3e4aSElliott Hughes if groupName.startswith("public.kern1."): 1004*e1fe3e4aSElliott Hughes d = firstSideMapping 1005*e1fe3e4aSElliott Hughes else: 1006*e1fe3e4aSElliott Hughes d = secondSideMapping 1007*e1fe3e4aSElliott Hughes for glyphName in glyphList: 1008*e1fe3e4aSElliott Hughes if not isinstance(glyphName, str): 1009*e1fe3e4aSElliott Hughes return ( 1010*e1fe3e4aSElliott Hughes False, 1011*e1fe3e4aSElliott Hughes "The group data %s contains an invalid member." % groupName, 1012*e1fe3e4aSElliott Hughes ) 1013*e1fe3e4aSElliott Hughes if glyphName in d: 1014*e1fe3e4aSElliott Hughes return ( 1015*e1fe3e4aSElliott Hughes False, 1016*e1fe3e4aSElliott Hughes 'The glyph "%s" occurs in too many kerning groups.' % glyphName, 1017*e1fe3e4aSElliott Hughes ) 1018*e1fe3e4aSElliott Hughes d[glyphName] = groupName 1019*e1fe3e4aSElliott Hughes return True, None 1020*e1fe3e4aSElliott Hughes 1021*e1fe3e4aSElliott Hughes 1022*e1fe3e4aSElliott Hughes# ------------- 1023*e1fe3e4aSElliott Hughes# kerning.plist 1024*e1fe3e4aSElliott Hughes# ------------- 1025*e1fe3e4aSElliott Hughes 1026*e1fe3e4aSElliott Hughes 1027*e1fe3e4aSElliott Hughesdef kerningValidator(data): 1028*e1fe3e4aSElliott Hughes """ 1029*e1fe3e4aSElliott Hughes Check the validity of the kerning data structure. 1030*e1fe3e4aSElliott Hughes Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). 1031*e1fe3e4aSElliott Hughes 1032*e1fe3e4aSElliott Hughes >>> kerning = {"A" : {"B" : 100}} 1033*e1fe3e4aSElliott Hughes >>> kerningValidator(kerning) 1034*e1fe3e4aSElliott Hughes (True, None) 1035*e1fe3e4aSElliott Hughes 1036*e1fe3e4aSElliott Hughes >>> kerning = {"A" : ["B"]} 1037*e1fe3e4aSElliott Hughes >>> valid, msg = kerningValidator(kerning) 1038*e1fe3e4aSElliott Hughes >>> valid 1039*e1fe3e4aSElliott Hughes False 1040*e1fe3e4aSElliott Hughes >>> print(msg) 1041*e1fe3e4aSElliott Hughes The kerning data is not in the correct format. 1042*e1fe3e4aSElliott Hughes 1043*e1fe3e4aSElliott Hughes >>> kerning = {"A" : {"B" : "100"}} 1044*e1fe3e4aSElliott Hughes >>> valid, msg = kerningValidator(kerning) 1045*e1fe3e4aSElliott Hughes >>> valid 1046*e1fe3e4aSElliott Hughes False 1047*e1fe3e4aSElliott Hughes >>> print(msg) 1048*e1fe3e4aSElliott Hughes The kerning data is not in the correct format. 1049*e1fe3e4aSElliott Hughes """ 1050*e1fe3e4aSElliott Hughes bogusFormatMessage = "The kerning data is not in the correct format." 1051*e1fe3e4aSElliott Hughes if not isinstance(data, Mapping): 1052*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 1053*e1fe3e4aSElliott Hughes for first, secondDict in data.items(): 1054*e1fe3e4aSElliott Hughes if not isinstance(first, str): 1055*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 1056*e1fe3e4aSElliott Hughes elif not isinstance(secondDict, Mapping): 1057*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 1058*e1fe3e4aSElliott Hughes for second, value in secondDict.items(): 1059*e1fe3e4aSElliott Hughes if not isinstance(second, str): 1060*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 1061*e1fe3e4aSElliott Hughes elif not isinstance(value, numberTypes): 1062*e1fe3e4aSElliott Hughes return False, bogusFormatMessage 1063*e1fe3e4aSElliott Hughes return True, None 1064*e1fe3e4aSElliott Hughes 1065*e1fe3e4aSElliott Hughes 1066*e1fe3e4aSElliott Hughes# ------------- 1067*e1fe3e4aSElliott Hughes# lib.plist/lib 1068*e1fe3e4aSElliott Hughes# ------------- 1069*e1fe3e4aSElliott Hughes 1070*e1fe3e4aSElliott Hughes_bogusLibFormatMessage = "The lib data is not in the correct format: %s" 1071*e1fe3e4aSElliott Hughes 1072*e1fe3e4aSElliott Hughes 1073*e1fe3e4aSElliott Hughesdef fontLibValidator(value): 1074*e1fe3e4aSElliott Hughes """ 1075*e1fe3e4aSElliott Hughes Check the validity of the lib. 1076*e1fe3e4aSElliott Hughes Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). 1077*e1fe3e4aSElliott Hughes 1078*e1fe3e4aSElliott Hughes >>> lib = {"foo" : "bar"} 1079*e1fe3e4aSElliott Hughes >>> fontLibValidator(lib) 1080*e1fe3e4aSElliott Hughes (True, None) 1081*e1fe3e4aSElliott Hughes 1082*e1fe3e4aSElliott Hughes >>> lib = {"public.awesome" : "hello"} 1083*e1fe3e4aSElliott Hughes >>> fontLibValidator(lib) 1084*e1fe3e4aSElliott Hughes (True, None) 1085*e1fe3e4aSElliott Hughes 1086*e1fe3e4aSElliott Hughes >>> lib = {"public.glyphOrder" : ["A", "C", "B"]} 1087*e1fe3e4aSElliott Hughes >>> fontLibValidator(lib) 1088*e1fe3e4aSElliott Hughes (True, None) 1089*e1fe3e4aSElliott Hughes 1090*e1fe3e4aSElliott Hughes >>> lib = "hello" 1091*e1fe3e4aSElliott Hughes >>> valid, msg = fontLibValidator(lib) 1092*e1fe3e4aSElliott Hughes >>> valid 1093*e1fe3e4aSElliott Hughes False 1094*e1fe3e4aSElliott Hughes >>> print(msg) # doctest: +ELLIPSIS 1095*e1fe3e4aSElliott Hughes The lib data is not in the correct format: expected a dictionary, ... 1096*e1fe3e4aSElliott Hughes 1097*e1fe3e4aSElliott Hughes >>> lib = {1: "hello"} 1098*e1fe3e4aSElliott Hughes >>> valid, msg = fontLibValidator(lib) 1099*e1fe3e4aSElliott Hughes >>> valid 1100*e1fe3e4aSElliott Hughes False 1101*e1fe3e4aSElliott Hughes >>> print(msg) 1102*e1fe3e4aSElliott Hughes The lib key is not properly formatted: expected str, found int: 1 1103*e1fe3e4aSElliott Hughes 1104*e1fe3e4aSElliott Hughes >>> lib = {"public.glyphOrder" : "hello"} 1105*e1fe3e4aSElliott Hughes >>> valid, msg = fontLibValidator(lib) 1106*e1fe3e4aSElliott Hughes >>> valid 1107*e1fe3e4aSElliott Hughes False 1108*e1fe3e4aSElliott Hughes >>> print(msg) # doctest: +ELLIPSIS 1109*e1fe3e4aSElliott Hughes public.glyphOrder is not properly formatted: expected list or tuple,... 1110*e1fe3e4aSElliott Hughes 1111*e1fe3e4aSElliott Hughes >>> lib = {"public.glyphOrder" : ["A", 1, "B"]} 1112*e1fe3e4aSElliott Hughes >>> valid, msg = fontLibValidator(lib) 1113*e1fe3e4aSElliott Hughes >>> valid 1114*e1fe3e4aSElliott Hughes False 1115*e1fe3e4aSElliott Hughes >>> print(msg) # doctest: +ELLIPSIS 1116*e1fe3e4aSElliott Hughes public.glyphOrder is not properly formatted: expected str,... 1117*e1fe3e4aSElliott Hughes """ 1118*e1fe3e4aSElliott Hughes if not isDictEnough(value): 1119*e1fe3e4aSElliott Hughes reason = "expected a dictionary, found %s" % type(value).__name__ 1120*e1fe3e4aSElliott Hughes return False, _bogusLibFormatMessage % reason 1121*e1fe3e4aSElliott Hughes for key, value in value.items(): 1122*e1fe3e4aSElliott Hughes if not isinstance(key, str): 1123*e1fe3e4aSElliott Hughes return False, ( 1124*e1fe3e4aSElliott Hughes "The lib key is not properly formatted: expected str, found %s: %r" 1125*e1fe3e4aSElliott Hughes % (type(key).__name__, key) 1126*e1fe3e4aSElliott Hughes ) 1127*e1fe3e4aSElliott Hughes # public.glyphOrder 1128*e1fe3e4aSElliott Hughes if key == "public.glyphOrder": 1129*e1fe3e4aSElliott Hughes bogusGlyphOrderMessage = "public.glyphOrder is not properly formatted: %s" 1130*e1fe3e4aSElliott Hughes if not isinstance(value, (list, tuple)): 1131*e1fe3e4aSElliott Hughes reason = "expected list or tuple, found %s" % type(value).__name__ 1132*e1fe3e4aSElliott Hughes return False, bogusGlyphOrderMessage % reason 1133*e1fe3e4aSElliott Hughes for glyphName in value: 1134*e1fe3e4aSElliott Hughes if not isinstance(glyphName, str): 1135*e1fe3e4aSElliott Hughes reason = "expected str, found %s" % type(glyphName).__name__ 1136*e1fe3e4aSElliott Hughes return False, bogusGlyphOrderMessage % reason 1137*e1fe3e4aSElliott Hughes return True, None 1138*e1fe3e4aSElliott Hughes 1139*e1fe3e4aSElliott Hughes 1140*e1fe3e4aSElliott Hughes# -------- 1141*e1fe3e4aSElliott Hughes# GLIF lib 1142*e1fe3e4aSElliott Hughes# -------- 1143*e1fe3e4aSElliott Hughes 1144*e1fe3e4aSElliott Hughes 1145*e1fe3e4aSElliott Hughesdef glyphLibValidator(value): 1146*e1fe3e4aSElliott Hughes """ 1147*e1fe3e4aSElliott Hughes Check the validity of the lib. 1148*e1fe3e4aSElliott Hughes Version 3+ (though it's backwards compatible with UFO 1 and UFO 2). 1149*e1fe3e4aSElliott Hughes 1150*e1fe3e4aSElliott Hughes >>> lib = {"foo" : "bar"} 1151*e1fe3e4aSElliott Hughes >>> glyphLibValidator(lib) 1152*e1fe3e4aSElliott Hughes (True, None) 1153*e1fe3e4aSElliott Hughes 1154*e1fe3e4aSElliott Hughes >>> lib = {"public.awesome" : "hello"} 1155*e1fe3e4aSElliott Hughes >>> glyphLibValidator(lib) 1156*e1fe3e4aSElliott Hughes (True, None) 1157*e1fe3e4aSElliott Hughes 1158*e1fe3e4aSElliott Hughes >>> lib = {"public.markColor" : "1,0,0,0.5"} 1159*e1fe3e4aSElliott Hughes >>> glyphLibValidator(lib) 1160*e1fe3e4aSElliott Hughes (True, None) 1161*e1fe3e4aSElliott Hughes 1162*e1fe3e4aSElliott Hughes >>> lib = {"public.markColor" : 1} 1163*e1fe3e4aSElliott Hughes >>> valid, msg = glyphLibValidator(lib) 1164*e1fe3e4aSElliott Hughes >>> valid 1165*e1fe3e4aSElliott Hughes False 1166*e1fe3e4aSElliott Hughes >>> print(msg) 1167*e1fe3e4aSElliott Hughes public.markColor is not properly formatted. 1168*e1fe3e4aSElliott Hughes """ 1169*e1fe3e4aSElliott Hughes if not isDictEnough(value): 1170*e1fe3e4aSElliott Hughes reason = "expected a dictionary, found %s" % type(value).__name__ 1171*e1fe3e4aSElliott Hughes return False, _bogusLibFormatMessage % reason 1172*e1fe3e4aSElliott Hughes for key, value in value.items(): 1173*e1fe3e4aSElliott Hughes if not isinstance(key, str): 1174*e1fe3e4aSElliott Hughes reason = "key (%s) should be a string" % key 1175*e1fe3e4aSElliott Hughes return False, _bogusLibFormatMessage % reason 1176*e1fe3e4aSElliott Hughes # public.markColor 1177*e1fe3e4aSElliott Hughes if key == "public.markColor": 1178*e1fe3e4aSElliott Hughes if not colorValidator(value): 1179*e1fe3e4aSElliott Hughes return False, "public.markColor is not properly formatted." 1180*e1fe3e4aSElliott Hughes return True, None 1181*e1fe3e4aSElliott Hughes 1182*e1fe3e4aSElliott Hughes 1183*e1fe3e4aSElliott Hughesif __name__ == "__main__": 1184*e1fe3e4aSElliott Hughes import doctest 1185*e1fe3e4aSElliott Hughes 1186*e1fe3e4aSElliott Hughes doctest.testmod() 1187