1# -*- coding: utf8 -*- 2 3from collections import Counter 4from fontTools.misc.fixedTools import otRound 5 6# ---- 7# Font 8# ---- 9 10 11def normalizeFileFormatVersion(value): 12 """ 13 Normalizes a font's file format version. 14 15 * **value** must be a :ref:`type-int`. 16 * Returned value will be a ``int``. 17 """ 18 if not isinstance(value, int): 19 raise TypeError("File format versions must be instances of " 20 ":ref:`type-int`, not %s." 21 % type(value).__name__) 22 return value 23 24 25def normalizeFileStructure(value): 26 """ 27 Normalizes a font's file structure. 28 29 * **value** must be a :ref:`type-string`. 30 * Returned value will be a ``string``. 31 """ 32 allowedFileStructures = ["zip", "package"] 33 if value not in allowedFileStructures: 34 raise TypeError("File Structure must be %s, not %s" % (", ".join(allowedFileStructures), value)) 35 return value 36 37 38def normalizeLayerOrder(value, font): 39 """ 40 Normalizes layer order. 41 42 ** **value** must be a ``tuple`` or ``list``. 43 * **value** items must normalize as layer names with 44 :func:`normalizeLayerName`. 45 * **value** must contain layers that exist in **font**. 46 * **value** must not contain duplicate layers. 47 * Returned ``tuple`` will be unencoded ``unicode`` strings 48 for each layer name. 49 """ 50 if not isinstance(value, (tuple, list)): 51 raise TypeError("Layer order must be a list, not %s." 52 % type(value).__name__) 53 for v in value: 54 normalizeLayerName(v) 55 fontLayers = [layer.name for layer in font.layers] 56 for name in value: 57 if name not in fontLayers: 58 raise ValueError("Layer must exist in font. %s does not exist " 59 "in font.layers." % name) 60 duplicates = [v for v, count in Counter(value).items() if count > 1] 61 if len(duplicates) != 0: 62 raise ValueError("Duplicate layers are not allowed. Layer name(s) " 63 "'%s' are duplicate(s)." % ", ".join(duplicates)) 64 return tuple(value) 65 66 67def normalizeDefaultLayerName(value, font): 68 """ 69 Normalizes default layer name. 70 71 * **value** must normalize as layer name with 72 :func:`normalizeLayerName`. 73 * **value** must be a layer in **font**. 74 * Returned value will be an unencoded ``unicode`` string. 75 """ 76 value = normalizeLayerName(value) 77 if value not in font.layerOrder: 78 raise ValueError("No layer with the name '%s' exists." % value) 79 return str(value) 80 81 82def normalizeGlyphOrder(value): 83 """ 84 Normalizes glyph order. 85 86 ** **value** must be a ``tuple`` or ``list``. 87 * **value** items must normalize as glyph names with 88 :func:`normalizeGlyphName`. 89 * **value** must not repeat glyph names. 90 * Returned value will be a ``tuple`` of unencoded ``unicode`` strings. 91 """ 92 if not isinstance(value, (tuple, list)): 93 raise TypeError("Glyph order must be a list, not %s." 94 % type(value).__name__) 95 for v in value: 96 normalizeGlyphName(v) 97 duplicates = sorted(v for v, count in Counter(value).items() if count > 1) 98 if len(duplicates) != 0: 99 raise ValueError("Duplicate glyph names are not allowed. Glyph " 100 "name(s) '%s' are duplicate." % ", ".join(duplicates)) 101 return tuple(value) 102 103 104# ------- 105# Kerning 106# ------- 107 108 109def normalizeKerningKey(value): 110 """ 111 Normalizes kerning key. 112 113 * **value** must be a ``tuple`` or ``list``. 114 * **value** must contain only two members. 115 * **value** items must be :ref:`type-string`. 116 * **value** items must be at least one character long. 117 * Returned value will be a two member ``tuple`` of unencoded 118 ``unicode`` strings. 119 """ 120 if not isinstance(value, (tuple, list)): 121 raise TypeError("Kerning key must be a tuple instance, not %s." 122 % type(value).__name__) 123 if len(value) != 2: 124 raise ValueError("Kerning key must be a tuple containing two items, " 125 "not %d." % len(value)) 126 for v in value: 127 if not isinstance(v, str): 128 raise TypeError("Kerning key items must be strings, not %s." 129 % type(v).__name__) 130 if len(v) < 1: 131 raise ValueError("Kerning key items must be at least one character long") 132 if value[0].startswith("public.") and not value[0].startswith( 133 "public.kern1."): 134 raise ValueError("Left Kerning key group must start with " 135 "public.kern1.") 136 if value[1].startswith("public.") and not value[1].startswith( 137 "public.kern2."): 138 raise ValueError("Right Kerning key group must start with " 139 "public.kern2.") 140 return tuple(value) 141 142 143def normalizeKerningValue(value): 144 """ 145 Normalizes kerning value. 146 147 * **value** must be an :ref:`type-int-float`. 148 * Returned value is the same type as input value. 149 """ 150 if not isinstance(value, (int, float)): 151 raise TypeError("Kerning value must be a int or a float, not %s." 152 % type(value).__name__) 153 return value 154 155 156# ------ 157# Groups 158# ------ 159 160def normalizeGroupKey(value): 161 """ 162 Normalizes group key. 163 164 * **value** must be a :ref:`type-string`. 165 * **value** must be least one character long. 166 * Returned value will be an unencoded ``unicode`` string. 167 """ 168 if not isinstance(value, str): 169 raise TypeError("Group key must be a string, not %s." 170 % type(value).__name__) 171 if len(value) < 1: 172 raise ValueError("Group key must be at least one character long.") 173 return value 174 175 176def normalizeGroupValue(value): 177 """ 178 Normalizes group value. 179 180 * **value** must be a ``list``. 181 * **value** items must normalize as glyph names with 182 :func:`normalizeGlyphName`. 183 * Returned value will be a ``tuple`` of unencoded ``unicode`` strings. 184 """ 185 if not isinstance(value, (tuple, list)): 186 raise TypeError("Group value must be a list, not %s." 187 % type(value).__name__) 188 value = [normalizeGlyphName(v) for v in value] 189 return tuple(value) 190 191 192# -------- 193# Features 194# -------- 195 196def normalizeFeatureText(value): 197 """ 198 Normalizes feature text. 199 200 * **value** must be a :ref:`type-string`. 201 * Returned value will be an unencoded ``unicode`` string. 202 """ 203 if not isinstance(value, str): 204 raise TypeError("Feature text must be a string, not %s." 205 % type(value).__name__) 206 return value 207 208 209# --- 210# Lib 211# --- 212 213def normalizeLibKey(value): 214 """ 215 Normalizes lib key. 216 217 * **value** must be a :ref:`type-string`. 218 * **value** must be at least one character long. 219 * Returned value will be an unencoded ``unicode`` string. 220 """ 221 if not isinstance(value, str): 222 raise TypeError("Lib key must be a string, not %s." 223 % type(value).__name__) 224 if len(value) < 1: 225 raise ValueError("Lib key must be at least one character.") 226 return value 227 228 229def normalizeLibValue(value): 230 """ 231 Normalizes lib value. 232 233 * **value** must not be ``None``. 234 * Returned value is the same type as the input value. 235 """ 236 if value is None: 237 raise ValueError("Lib value must not be None.") 238 if isinstance(value, (list, tuple)): 239 for v in value: 240 normalizeLibValue(v) 241 elif isinstance(value, dict): 242 for k, v in value.items(): 243 normalizeLibKey(k) 244 normalizeLibValue(v) 245 return value 246 247 248# ----- 249# Layer 250# ----- 251 252def normalizeLayer(value): 253 """ 254 Normalizes layer. 255 256 * **value** must be a instance of :class:`BaseLayer` 257 * Returned value is the same type as the input value. 258 """ 259 from fontParts.base.layer import BaseLayer 260 return normalizeInternalObjectType(value, BaseLayer, "Layer") 261 262 263def normalizeLayerName(value): 264 """ 265 Normalizes layer name. 266 267 * **value** must be a :ref:`type-string`. 268 * **value** must be at least one character long. 269 * Returned value will be an unencoded ``unicode`` string. 270 """ 271 if not isinstance(value, str): 272 raise TypeError("Layer names must be strings, not %s." 273 % type(value).__name__) 274 if len(value) < 1: 275 raise ValueError("Layer names must be at least one character long.") 276 return value 277 278 279# ----- 280# Glyph 281# ----- 282 283def normalizeGlyph(value): 284 """ 285 Normalizes glyph. 286 287 * **value** must be a instance of :class:`BaseGlyph` 288 * Returned value is the same type as the input value. 289 """ 290 from fontParts.base.glyph import BaseGlyph 291 return normalizeInternalObjectType(value, BaseGlyph, "Glyph") 292 293 294def normalizeGlyphName(value): 295 """ 296 Normalizes glyph name. 297 298 * **value** must be a :ref:`type-string`. 299 * **value** must be at least one character long. 300 * Returned value will be an unencoded ``unicode`` string. 301 """ 302 if not isinstance(value, str): 303 raise TypeError("Glyph names must be strings, not %s." 304 % type(value).__name__) 305 if len(value) < 1: 306 raise ValueError("Glyph names must be at least one character long.") 307 return value 308 309 310def normalizeGlyphUnicodes(value): 311 """ 312 Normalizes glyph unicodes. 313 314 * **value** must be a ``list``. 315 * **value** items must normalize as glyph unicodes with 316 :func:`normalizeGlyphUnicode`. 317 * **value** must not repeat unicode values. 318 * Returned value will be a ``tuple`` of ints. 319 """ 320 if not isinstance(value, (tuple, list)): 321 raise TypeError("Glyph unicodes must be a list, not %s." 322 % type(value).__name__) 323 values = [normalizeGlyphUnicode(v) for v in value] 324 duplicates = [v for v, count in Counter(value).items() if count > 1] 325 if len(duplicates) != 0: 326 raise ValueError("Duplicate unicode values are not allowed.") 327 return tuple(values) 328 329 330def normalizeGlyphUnicode(value): 331 """ 332 Normalizes glyph unicode. 333 334 * **value** must be an int or hex (represented as a string). 335 * **value** must be in a unicode range. 336 * Returned value will be an ``int``. 337 """ 338 if not isinstance(value, (int, str)) or isinstance(value, bool): 339 raise TypeError("Glyph unicode must be a int or hex string, not %s." 340 % type(value).__name__) 341 if isinstance(value, str): 342 try: 343 value = int(value, 16) 344 except ValueError: 345 raise ValueError("Glyph unicode hex must be a valid hex string.") 346 if value < 0 or value > 1114111: 347 raise ValueError("Glyph unicode must be in the Unicode range.") 348 return value 349 350 351def normalizeGlyphWidth(value): 352 """ 353 Normalizes glyph width. 354 355 * **value** must be a :ref:`type-int-float`. 356 * Returned value is the same type as the input value. 357 """ 358 if not isinstance(value, (int, float)): 359 raise TypeError("Glyph width must be an :ref:`type-int-float`, not %s." 360 % type(value).__name__) 361 return value 362 363 364def normalizeGlyphLeftMargin(value): 365 """ 366 Normalizes glyph left margin. 367 368 * **value** must be a :ref:`type-int-float` or `None`. 369 * Returned value is the same type as the input value. 370 """ 371 if not isinstance(value, (int, float)) and value is not None: 372 raise TypeError("Glyph left margin must be an :ref:`type-int-float`, " 373 "not %s." % type(value).__name__) 374 return value 375 376 377def normalizeGlyphRightMargin(value): 378 """ 379 Normalizes glyph right margin. 380 381 * **value** must be a :ref:`type-int-float` or `None`. 382 * Returned value is the same type as the input value. 383 """ 384 if not isinstance(value, (int, float)) and value is not None: 385 raise TypeError("Glyph right margin must be an :ref:`type-int-float`, " 386 "not %s." % type(value).__name__) 387 return value 388 389 390def normalizeGlyphHeight(value): 391 """ 392 Normalizes glyph height. 393 394 * **value** must be a :ref:`type-int-float`. 395 * Returned value is the same type as the input value. 396 """ 397 if not isinstance(value, (int, float)): 398 raise TypeError("Glyph height must be an :ref:`type-int-float`, not " 399 "%s." % type(value).__name__) 400 return value 401 402 403def normalizeGlyphBottomMargin(value): 404 """ 405 Normalizes glyph bottom margin. 406 407 * **value** must be a :ref:`type-int-float` or `None`. 408 * Returned value is the same type as the input value. 409 """ 410 if not isinstance(value, (int, float)) and value is not None: 411 raise TypeError("Glyph bottom margin must be an " 412 ":ref:`type-int-float`, not %s." 413 % type(value).__name__) 414 return value 415 416 417def normalizeGlyphTopMargin(value): 418 """ 419 Normalizes glyph top margin. 420 421 * **value** must be a :ref:`type-int-float` or `None`. 422 * Returned value is the same type as the input value. 423 """ 424 if not isinstance(value, (int, float)) and value is not None: 425 raise TypeError("Glyph top margin must be an :ref:`type-int-float`, " 426 "not %s." % type(value).__name__) 427 return value 428 429 430def normalizeGlyphFormatVersion(value): 431 """ 432 Normalizes glyph format version for saving to XML string. 433 434 * **value** must be a :ref:`type-int-float` of either 1 or 2. 435 * Returned value will be an int. 436 """ 437 if not isinstance(value, (int, float)): 438 raise TypeError("Glyph Format Version must be an " 439 ":ref:`type-int-float`, not %s." 440 % type(value).__name__) 441 value = int(value) 442 if value not in (1, 2): 443 raise ValueError("Glyph Format Version must be either 1 or 2, not %s." 444 % value) 445 return value 446 447# ------- 448# Contour 449# ------- 450 451 452def normalizeContour(value): 453 """ 454 Normalizes contour. 455 456 * **value** must be a instance of :class:`BaseContour` 457 * Returned value is the same type as the input value. 458 """ 459 from fontParts.base.contour import BaseContour 460 return normalizeInternalObjectType(value, BaseContour, "Contour") 461 462 463# ----- 464# Point 465# ----- 466 467def normalizePointType(value): 468 """ 469 Normalizes point type. 470 471 * **value** must be an string. 472 * **value** must be one of the following: 473 474 +----------+ 475 | move | 476 +----------+ 477 | line | 478 +----------+ 479 | offcurve | 480 +----------+ 481 | curve | 482 +----------+ 483 | qcurve | 484 +----------+ 485 486 * Returned value will be an unencoded ``unicode`` string. 487 """ 488 allowedTypes = ['move', 'line', 'offcurve', 'curve', 'qcurve'] 489 if not isinstance(value, str): 490 raise TypeError("Point type must be a string, not %s." 491 % type(value).__name__) 492 if value not in allowedTypes: 493 raise ValueError("Point type must be '%s'; not %r." 494 % ("', '".join(allowedTypes), value)) 495 return value 496 497 498def normalizePointName(value): 499 """ 500 Normalizes point name. 501 502 * **value** must be a :ref:`type-string`. 503 * **value** must be at least one character long. 504 * Returned value will be an unencoded ``unicode`` string. 505 """ 506 if not isinstance(value, str): 507 raise TypeError("Point names must be strings, not %s." 508 % type(value).__name__) 509 if len(value) < 1: 510 raise ValueError("Point names must be at least one character long.") 511 return value 512 513 514def normalizePoint(value): 515 """ 516 Normalizes point. 517 518 * **value** must be a instance of :class:`BasePoint` 519 * Returned value is the same type as the input value. 520 """ 521 from fontParts.base.point import BasePoint 522 return normalizeInternalObjectType(value, BasePoint, "Point") 523 524# ------- 525# Segment 526# ------- 527 528 529def normalizeSegment(value): 530 """ 531 Normalizes segment. 532 533 * **value** must be a instance of :class:`BaseSegment` 534 * Returned value is the same type as the input value. 535 """ 536 from fontParts.base.segment import BaseSegment 537 return normalizeInternalObjectType(value, BaseSegment, "Segment") 538 539 540def normalizeSegmentType(value): 541 """ 542 Normalizes segment type. 543 544 * **value** must be a :ref:`type-string`. 545 * **value** must be one of the following: 546 547 +--------+ 548 | move | 549 +--------+ 550 | line | 551 +--------+ 552 | curve | 553 +--------+ 554 | qcurve | 555 +--------+ 556 557 * Returned value will be an unencoded ``unicode`` string. 558 """ 559 allowedTypes = ['move', 'line', 'curve', 'qcurve'] 560 if not isinstance(value, str): 561 raise TypeError("Segment type must be a string, not %s." 562 % type(value).__name__) 563 if value not in allowedTypes: 564 raise ValueError("Segment type must be '%s'; not %r." 565 % ("', '".join(allowedTypes), value)) 566 return value 567 568 569# ------ 570# BPoint 571# ------ 572 573def normalizeBPoint(value): 574 """ 575 Normalizes bPoint. 576 577 * **value** must be a instance of :class:`BaseBPoint` 578 * Returned value is the same type as the input value. 579 """ 580 from fontParts.base.bPoint import BaseBPoint 581 return normalizeInternalObjectType(value, BaseBPoint, "bPoint") 582 583 584def normalizeBPointType(value): 585 """ 586 Normalizes bPoint type. 587 588 * **value** must be an string. 589 * **value** must be one of the following: 590 591 +--------+ 592 | corner | 593 +--------+ 594 | curve | 595 +--------+ 596 597 * Returned value will be an unencoded ``unicode`` string. 598 """ 599 allowedTypes = ['corner', 'curve'] 600 if not isinstance(value, str): 601 raise TypeError("bPoint type must be a string, not %s." 602 % type(value).__name__) 603 if value not in allowedTypes: 604 raise ValueError("bPoint type must be 'corner' or 'curve', not %r." 605 % value) 606 return value 607 608 609# --------- 610# Component 611# --------- 612 613def normalizeComponent(value): 614 """ 615 Normalizes component. 616 617 * **value** must be a instance of :class:`BaseComponent` 618 * Returned value is the same type as the input value. 619 """ 620 from fontParts.base.component import BaseComponent 621 return normalizeInternalObjectType(value, BaseComponent, "Component") 622 623 624def normalizeComponentScale(value): 625 """ 626 Normalizes component scale. 627 628 * **value** must be a `tuple`` or ``list``. 629 * **value** must have exactly two items. 630 These items must be instances of :ref:`type-int-float`. 631 * Returned value is a ``tuple`` of two ``float``\s. 632 """ 633 if not isinstance(value, (list, tuple)): 634 raise TypeError("Component scale must be a tuple " 635 "instance, not %s." % type(value).__name__) 636 else: 637 if not len(value) == 2: 638 raise ValueError("Transformation scale tuple must contain two " 639 "values, not %d." % len(value)) 640 for v in value: 641 if not isinstance(v, (int, float)): 642 raise TypeError("Transformation scale tuple values must be an " 643 ":ref:`type-int-float`, not %s." 644 % type(value).__name__) 645 value = tuple([float(v) for v in value]) 646 return value 647 648 649# ------ 650# Anchor 651# ------ 652 653def normalizeAnchor(value): 654 """ 655 Normalizes anchor. 656 657 * **value** must be a instance of :class:`BaseAnchor` 658 * Returned value is the same type as the input value. 659 """ 660 from fontParts.base.anchor import BaseAnchor 661 return normalizeInternalObjectType(value, BaseAnchor, "Anchor") 662 663 664def normalizeAnchorName(value): 665 """ 666 Normalizes anchor name. 667 668 * **value** must be a :ref:`type-string` or ``None``. 669 * **value** must be at least one character long if :ref:`type-string`. 670 * Returned value will be an unencoded ``unicode`` string or ``None``. 671 """ 672 if value is None: 673 return None 674 if not isinstance(value, str): 675 raise TypeError("Anchor names must be strings, not %s." 676 % type(value).__name__) 677 if len(value) < 1: 678 raise ValueError(("Anchor names must be at least one character " 679 "long or None.")) 680 return value 681 682 683# --------- 684# Guideline 685# --------- 686 687def normalizeGuideline(value): 688 """ 689 Normalizes guideline. 690 691 * **value** must be a instance of :class:`BaseGuideline` 692 * Returned value is the same type as the input value. 693 """ 694 from fontParts.base.guideline import BaseGuideline 695 return normalizeInternalObjectType(value, BaseGuideline, "Guideline") 696 697 698def normalizeGuidelineName(value): 699 """ 700 Normalizes guideline name. 701 702 * **value** must be a :ref:`type-string`. 703 * **value** must be at least one character long. 704 * Returned value will be an unencoded ``unicode`` string. 705 """ 706 if not isinstance(value, str): 707 raise TypeError("Guideline names must be strings, not %s." 708 % type(value).__name__) 709 if len(value) < 1: 710 raise ValueError("Guideline names must be at least one character " 711 "long.") 712 return value 713 714 715# ------- 716# Generic 717# ------- 718 719def normalizeInternalObjectType(value, cls, name): 720 """ 721 Normalizes an internal object type. 722 723 * **value** must be a instance of **cls**. 724 * Returned value is the same type as the input value. 725 """ 726 if not isinstance(value, cls): 727 raise TypeError("%s must be a %s instance, not %s." 728 % (name, name, type(value).__name__)) 729 return value 730 731 732def normalizeBoolean(value): 733 """ 734 Normalizes a boolean. 735 736 * **value** must be an ``int`` with value of 0 or 1, or a ``bool``. 737 * Returned value will be a boolean. 738 """ 739 if isinstance(value, int) and value in (0, 1): 740 value = bool(value) 741 if not isinstance(value, bool): 742 raise ValueError("Boolean values must be True or False, not '%s'." 743 % value) 744 return value 745 746 747# Identification 748 749def normalizeIndex(value): 750 """ 751 Normalizes index. 752 753 * **value** must be an ``int`` or ``None``. 754 * Returned value is the same type as the input value. 755 """ 756 if value is not None: 757 if not isinstance(value, int): 758 raise TypeError("Indexes must be None or integers, not %s." 759 % type(value).__name__) 760 return value 761 762 763def normalizeIdentifier(value): 764 """ 765 Normalizes identifier. 766 767 * **value** must be an :ref:`type-string` or `None`. 768 * **value** must not be longer than 100 characters. 769 * **value** must not contain a character out the range of 0x20 - 0x7E. 770 * Returned value is an unencoded ``unicode`` string. 771 """ 772 if value is None: 773 return value 774 if not isinstance(value, str): 775 raise TypeError("Identifiers must be strings, not %s." 776 % type(value).__name__) 777 if len(value) == 0: 778 raise ValueError("The identifier string is empty.") 779 if len(value) > 100: 780 raise ValueError("The identifier string has a length (%d) greater " 781 "than the maximum allowed (100)." % len(value)) 782 for c in value: 783 v = ord(c) 784 if v < 0x20 or v > 0x7E: 785 raise ValueError("The identifier string ('%s') contains a " 786 "character out size of the range 0x20 - 0x7E." 787 % value) 788 return value 789 790 791# Coordinates 792 793def normalizeX(value): 794 """ 795 Normalizes x coordinate. 796 797 * **value** must be an :ref:`type-int-float`. 798 * Returned value is the same type as the input value. 799 """ 800 if not isinstance(value, (int, float)): 801 raise TypeError("X coordinates must be instances of " 802 ":ref:`type-int-float`, not %s." 803 % type(value).__name__) 804 return value 805 806 807def normalizeY(value): 808 """ 809 Normalizes y coordinate. 810 811 * **value** must be an :ref:`type-int-float`. 812 * Returned value is the same type as the input value. 813 """ 814 if not isinstance(value, (int, float)): 815 raise TypeError("Y coordinates must be instances of " 816 ":ref:`type-int-float`, not %s." 817 % type(value).__name__) 818 return value 819 820 821def normalizeCoordinateTuple(value): 822 """ 823 Normalizes coordinate tuple. 824 825 * **value** must be a ``tuple`` or ``list``. 826 * **value** must have exactly two items. 827 * **value** items must be an :ref:`type-int-float`. 828 * Returned value is a ``tuple`` of two values of the same type as 829 the input values. 830 """ 831 if not isinstance(value, (tuple, list)): 832 raise TypeError("Coordinates must be tuple instances, not %s." 833 % type(value).__name__) 834 if len(value) != 2: 835 raise ValueError("Coordinates must be tuples containing two items, " 836 "not %d." % len(value)) 837 x, y = value 838 x = normalizeX(x) 839 y = normalizeY(y) 840 return (x, y) 841 842 843def normalizeBoundingBox(value): 844 """ 845 Normalizes bounding box. 846 847 * **value** must be an ``tuple`` or ``list``. 848 * **value** must have exactly four items. 849 * **value** items must be :ref:`type-int-float`. 850 * xMin and yMin must be less than or equal to the corresponding xMax, yMax. 851 * Returned value will be a tuple of four ``float``. 852 """ 853 if not isinstance(value, (tuple, list)): 854 raise TypeError("Bounding box be tuple instances, not %s." 855 % type(value).__name__) 856 if len(value) != 4: 857 raise ValueError("Bounding box be tuples containing four items, not " 858 "%d." % len(value)) 859 for v in value: 860 if not isinstance(v, (int, float)): 861 raise TypeError("Bounding box values must be instances of " 862 ":ref:`type-int-float`, not %s." 863 % type(value).__name__) 864 if value[0] > value[2]: 865 raise ValueError("Bounding box xMin must be less than or equal to " 866 "xMax.") 867 if value[1] > value[3]: 868 raise ValueError("Bounding box yMin must be less than or equal to " 869 "yMax.") 870 return tuple([float(v) for v in value]) 871 872 873def normalizeArea(value): 874 """ 875 Normalizes area. 876 877 * **value** must be a positive :ref:`type-int-float`. 878 """ 879 if not isinstance(value, (int, float)): 880 raise TypeError("Area must be an instance of :ref:`type-int-float`, " 881 "not %s." % type(value).__name__) 882 if value < 0: 883 raise ValueError("Area must be a positive :ref:`type-int-float`, " 884 "not %s." % repr(value)) 885 return float(value) 886 887 888def normalizeRotationAngle(value): 889 """ 890 Normalizes an angle. 891 892 * Value must be a :ref:`type-int-float`. 893 * Value must be between -360 and 360. 894 * If the value is negative, it is normalized by adding it to 360 895 * Returned value is a ``float`` between 0 and 360. 896 """ 897 if not isinstance(value, (int, float)): 898 raise TypeError("Angle must be instances of " 899 ":ref:`type-int-float`, not %s." 900 % type(value).__name__) 901 if abs(value) > 360: 902 raise ValueError("Angle must be between -360 and 360.") 903 if value < 0: 904 value = value + 360 905 return float(value) 906 907 908# Color 909 910def normalizeColor(value): 911 """ 912 Normalizes :ref:`type-color`. 913 914 * **value** must be an ``tuple`` or ``list``. 915 * **value** must have exactly four items. 916 * **value** color components must be between 0 and 1. 917 * Returned value is a ``tuple`` containing four ``float`` values. 918 """ 919 from fontParts.base.color import Color 920 if not isinstance(value, (tuple, list, Color)): 921 raise TypeError("Colors must be tuple instances, not %s." 922 % type(value).__name__) 923 if not len(value) == 4: 924 raise ValueError("Colors must contain four values, not %d." 925 % len(value)) 926 for component, v in zip("rgba", value): 927 if not isinstance(v, (int, float)): 928 raise TypeError("The value for the %s component (%s) is not " 929 "an int or float." % (component, v)) 930 if v < 0 or v > 1: 931 raise ValueError("The value for the %s component (%s) is not " 932 "between 0 and 1." % (component, v)) 933 return tuple([float(v) for v in value]) 934 935 936# Note 937 938def normalizeGlyphNote(value): 939 """ 940 Normalizes Glyph Note. 941 942 * **value** must be a :ref:`type-string`. 943 * Returned value is an unencoded ``unicode`` string 944 """ 945 if not isinstance(value, str): 946 raise TypeError("Note must be a string, not %s." 947 % type(value).__name__) 948 return value 949 950 951# File Path 952 953def normalizeFilePath(value): 954 """ 955 Normalizes file path. 956 957 * **value** must be a :ref:`type-string`. 958 * Returned value is an unencoded ``unicode`` string 959 """ 960 if not isinstance(value, str): 961 raise TypeError("File paths must be strings, not %s." 962 % type(value).__name__) 963 return value 964 965 966# Interpolation 967 968def normalizeInterpolationFactor(value): 969 """ 970 Normalizes interpolation factor. 971 972 * **value** must be an :ref:`type-int-float`, ``tuple`` or ``list``. 973 * If **value** is a ``tuple`` or ``list``, it must have exactly two items. 974 These items must be instances of :ref:`type-int-float`. 975 * Returned value is a ``tuple`` of two ``float``. 976 """ 977 if not isinstance(value, (int, float, list, tuple)): 978 raise TypeError("Interpolation factor must be an int, float, or tuple " 979 "instances, not %s." % type(value).__name__) 980 if isinstance(value, (int, float)): 981 value = (float(value), float(value)) 982 else: 983 if not len(value) == 2: 984 raise ValueError("Interpolation factor tuple must contain two " 985 "values, not %d." % len(value)) 986 for v in value: 987 if not isinstance(v, (int, float)): 988 raise TypeError("Interpolation factor tuple values must be an " 989 ":ref:`type-int-float`, not %s." 990 % type(value).__name__) 991 value = tuple([float(v) for v in value]) 992 return value 993 994 995# --------------- 996# Transformations 997# --------------- 998 999def normalizeTransformationMatrix(value): 1000 """ 1001 Normalizes transformation matrix. 1002 1003 * **value** must be an ``tuple`` or ``list``. 1004 * **value** must have exactly six items. Each of these 1005 items must be an instance of :ref:`type-int-float`. 1006 * Returned value is a ``tuple`` of six ``float``. 1007 """ 1008 if not isinstance(value, (tuple, list)): 1009 raise TypeError("Transformation matrices must be tuple instances, " 1010 "not %s." % type(value).__name__) 1011 if not len(value) == 6: 1012 raise ValueError("Transformation matrices must contain six values, " 1013 "not %d." % len(value)) 1014 for v in value: 1015 if not isinstance(v, (int, float)): 1016 raise TypeError("Transformation matrix values must be instances " 1017 "of :ref:`type-int-float`, not %s." 1018 % type(v).__name__) 1019 return tuple([float(v) for v in value]) 1020 1021 1022def normalizeTransformationOffset(value): 1023 """ 1024 Normalizes transformation offset. 1025 1026 * **value** must be an ``tuple``. 1027 * **value** must have exactly two items. Each item 1028 must be an instance of :ref:`type-int-float`. 1029 * Returned value is a ``tuple`` of two ``float``. 1030 """ 1031 return normalizeCoordinateTuple(value) 1032 1033 1034def normalizeTransformationSkewAngle(value): 1035 """ 1036 Normalizes transformation skew angle. 1037 1038 * **value** must be an :ref:`type-int-float`, ``tuple`` or ``list``. 1039 * If **value** is a ``tuple`` or ``list``, it must have exactly two items. 1040 These items must be instances of :ref:`type-int-float`. 1041 * **value** items must be between -360 and 360. 1042 * If the value is negative, it is normalized by adding it to 360 1043 * Returned value is a ``tuple`` of two ``float`` between 0 and 360. 1044 """ 1045 if not isinstance(value, (int, float, list, tuple)): 1046 raise TypeError("Transformation skew angle must be an int, float, or " 1047 "tuple instances, not %s." % type(value).__name__) 1048 if isinstance(value, (int, float)): 1049 value = (float(value), 0) 1050 else: 1051 if not len(value) == 2: 1052 raise ValueError("Transformation skew angle tuple must contain " 1053 "two values, not %d." % len(value)) 1054 for v in value: 1055 if not isinstance(v, (int, float)): 1056 raise TypeError("Transformation skew angle tuple values must " 1057 "be an :ref:`type-int-float`, not %s." 1058 % type(value).__name__) 1059 value = tuple([float(v) for v in value]) 1060 for v in value: 1061 if abs(v) > 360: 1062 raise ValueError("Transformation skew angle must be between -360 " 1063 "and 360.") 1064 return tuple([float(v + 360) if v < 0 else float(v) for v in value]) 1065 1066 1067def normalizeTransformationScale(value): 1068 """ 1069 Normalizes transformation scale. 1070 1071 * **value** must be an :ref:`type-int-float`, ``tuple`` or ``list``. 1072 * If **value** is a ``tuple`` or ``list``, it must have exactly two items. 1073 These items must be instances of :ref:`type-int-float`. 1074 * Returned value is a ``tuple`` of two ``float``\s. 1075 """ 1076 if not isinstance(value, (int, float, list, tuple)): 1077 raise TypeError("Transformation scale must be an int, float, or tuple " 1078 "instances, not %s." % type(value).__name__) 1079 if isinstance(value, (int, float)): 1080 value = (float(value), float(value)) 1081 else: 1082 if not len(value) == 2: 1083 raise ValueError("Transformation scale tuple must contain two " 1084 "values, not %d." % len(value)) 1085 for v in value: 1086 if not isinstance(v, (int, float)): 1087 raise TypeError("Transformation scale tuple values must be an " 1088 ":ref:`type-int-float`, not %s." 1089 % type(value).__name__) 1090 value = tuple([float(v) for v in value]) 1091 return value 1092 1093 1094def normalizeVisualRounding(value): 1095 """ 1096 Normalizes rounding. 1097 Python 3 uses banker’s rounding, meaning anything that is at 0.5 1098 will go to the even number. This isn't always ideal for point 1099 coordinates, so instead round to the higher number. 1100 1101 * **value** must be an :ref:`type-int-float` 1102 * Returned value is a ``int`` 1103 """ 1104 1105 if not isinstance(value, (int, float)): 1106 raise TypeError("Value to round must be an int or float, not %s." 1107 % type(value).__name__) 1108 return otRound(value) 1109