Source code for ufoLib2.objects.info

from enum import IntEnum
from typing import Any, List, Optional, Sequence, Type, TypeVar, Union

import attr
from attr import define, field
from fontTools.ufoLib import UFOReader

from ufoLib2.objects.guideline import Guideline
from ufoLib2.objects.misc import AttrDictMixin

__all__ = ("Info", "GaspRangeRecord", "NameRecord", "WidthClass")


def _positive(instance: Any, attribute: Any, value: int) -> None:
    if value < 0:
        raise ValueError(
            "'{name}' must be at least 0 (got {value!r})".format(
                name=attribute.name, value=value
            )
        )


_optional_positive = attr.validators.optional(_positive)


# or maybe use IntFlag?
class GaspBehavior(IntEnum):
    GRIDFIT = 0
    DOGRAY = 1
    SYMMETRIC_GRIDFIT = 2
    SYMMETRIC_SMOOTHING = 3


def _convert_GaspBehavior(
    seq: Sequence[Union[GaspBehavior, int]]
) -> List[GaspBehavior]:
    return [v if isinstance(v, GaspBehavior) else GaspBehavior(v) for v in seq]


@define
class GaspRangeRecord(AttrDictMixin):
    rangeMaxPPEM: int = field(validator=_positive)
    # Use Set[GaspBehavior] instead of List?
    rangeGaspBehavior: List[GaspBehavior] = field(converter=_convert_GaspBehavior)


@define
class NameRecord(AttrDictMixin):
    nameID: int = field(validator=_positive)
    platformID: int = field(validator=_positive)
    encodingID: int = field(validator=_positive)
    languageID: int = field(validator=_positive)
    string: str = ""


class WidthClass(IntEnum):
    ULTRA_CONDENSED = 1
    EXTRA_CONDESED = 2
    CONDENSED = 3
    SEMI_CONDENSED = 4
    NORMAL = 5  # alias for WidthClass.MEDIUM
    MEDIUM = 5
    SEMI_EXPANDED = 6
    EXPANDED = 7
    EXTRA_EXPANDED = 8
    ULTRA_EXPANDED = 9


Tc = TypeVar("Tc", Guideline, GaspRangeRecord, NameRecord)


def _convert_optional_list(
    lst: Optional[Sequence[Any]], klass: Type[Tc]
) -> Optional[List[Tc]]:
    if lst is None:
        return None
    result = []
    for d in lst:
        if isinstance(d, klass):
            result.append(d)
        else:
            result.append(klass(**d))
    return result


def _convert_guidelines(
    values: Optional[Sequence[Union[Guideline, Any]]]
) -> Optional[List[Guideline]]:
    return _convert_optional_list(values, Guideline)


def _convert_gasp_range_records(
    values: Optional[Sequence[Union[GaspRangeRecord, Any]]]
) -> Optional[List[GaspRangeRecord]]:
    return _convert_optional_list(values, GaspRangeRecord)


def _convert_name_records(
    values: Optional[Sequence[Union[NameRecord, Any]]]
) -> Optional[List[NameRecord]]:
    return _convert_optional_list(values, NameRecord)


def _convert_WidthClass(value: Optional[int]) -> Optional[WidthClass]:
    return None if value is None else WidthClass(value)


[docs]@define class Info: """A data class representing the contents of fontinfo.plist. The attributes are formally specified at http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/. Value validation is mostly done during saving and loading. """ familyName: Optional[str] = None styleName: Optional[str] = None styleMapFamilyName: Optional[str] = None styleMapStyleName: Optional[str] = None versionMajor: Optional[int] = field(default=None, validator=_optional_positive) versionMinor: Optional[int] = field(default=None, validator=_optional_positive) copyright: Optional[str] = None trademark: Optional[str] = None unitsPerEm: Optional[float] = field(default=None, validator=_optional_positive) descender: Optional[float] = None xHeight: Optional[float] = None capHeight: Optional[float] = None ascender: Optional[float] = None italicAngle: Optional[float] = None note: Optional[str] = None _guidelines: Optional[List[Guideline]] = field( default=None, converter=_convert_guidelines ) @property def guidelines(self) -> Optional[List[Guideline]]: return self._guidelines @guidelines.setter def guidelines(self, value: Optional[List[Guideline]]) -> None: self._guidelines = _convert_guidelines(value) _openTypeGaspRangeRecords: Optional[List[GaspRangeRecord]] = field( default=None, converter=_convert_gasp_range_records ) @property def openTypeGaspRangeRecords(self) -> Optional[List[GaspRangeRecord]]: return self._openTypeGaspRangeRecords @openTypeGaspRangeRecords.setter def openTypeGaspRangeRecords(self, value: Optional[List[GaspRangeRecord]]) -> None: self._openTypeGaspRangeRecords = _convert_gasp_range_records(value) openTypeHeadCreated: Optional[str] = None openTypeHeadLowestRecPPEM: Optional[int] = field( default=None, validator=_optional_positive ) openTypeHeadFlags: Optional[List[int]] = None openTypeHheaAscender: Optional[int] = None openTypeHheaDescender: Optional[int] = None openTypeHheaLineGap: Optional[int] = None openTypeHheaCaretSlopeRise: Optional[int] = None openTypeHheaCaretSlopeRun: Optional[int] = None openTypeHheaCaretOffset: Optional[int] = None openTypeNameDesigner: Optional[str] = None openTypeNameDesignerURL: Optional[str] = None openTypeNameManufacturer: Optional[str] = None openTypeNameManufacturerURL: Optional[str] = None openTypeNameLicense: Optional[str] = None openTypeNameLicenseURL: Optional[str] = None openTypeNameVersion: Optional[str] = None openTypeNameUniqueID: Optional[str] = None openTypeNameDescription: Optional[str] = None openTypeNamePreferredFamilyName: Optional[str] = None openTypeNamePreferredSubfamilyName: Optional[str] = None openTypeNameCompatibleFullName: Optional[str] = None openTypeNameSampleText: Optional[str] = None openTypeNameWWSFamilyName: Optional[str] = None openTypeNameWWSSubfamilyName: Optional[str] = None _openTypeNameRecords: Optional[List[NameRecord]] = field( default=None, converter=_convert_name_records ) @property def openTypeNameRecords(self) -> Optional[List[NameRecord]]: return self._openTypeNameRecords @openTypeNameRecords.setter def openTypeNameRecords(self, value: Optional[List[NameRecord]]) -> None: self._openTypeNameRecords = _convert_name_records(value) _openTypeOS2WidthClass: Optional[WidthClass] = field( default=None, converter=_convert_WidthClass ) @property def openTypeOS2WidthClass(self) -> Optional[WidthClass]: return self._openTypeOS2WidthClass @openTypeOS2WidthClass.setter def openTypeOS2WidthClass(self, value: Optional[WidthClass]) -> None: self._openTypeOS2WidthClass = value if value is None else WidthClass(value) openTypeOS2WeightClass: Optional[int] = field(default=None) @openTypeOS2WeightClass.validator def _validate_weight_class(self, attribute: Any, value: Optional[int]) -> None: if value is not None and (value < 1 or value > 1000): raise ValueError("'openTypeOS2WeightClass' must be between 1 and 1000") openTypeOS2Selection: Optional[List[int]] = None openTypeOS2VendorID: Optional[str] = None openTypeOS2Panose: Optional[List[int]] = None openTypeOS2FamilyClass: Optional[List[int]] = None openTypeOS2UnicodeRanges: Optional[List[int]] = None openTypeOS2CodePageRanges: Optional[List[int]] = None openTypeOS2TypoAscender: Optional[int] = None openTypeOS2TypoDescender: Optional[int] = None openTypeOS2TypoLineGap: Optional[int] = None openTypeOS2WinAscent: Optional[int] = field( default=None, validator=_optional_positive ) openTypeOS2WinDescent: Optional[int] = field( default=None, validator=_optional_positive ) openTypeOS2Type: Optional[List[int]] = None openTypeOS2SubscriptXSize: Optional[int] = None openTypeOS2SubscriptYSize: Optional[int] = None openTypeOS2SubscriptXOffset: Optional[int] = None openTypeOS2SubscriptYOffset: Optional[int] = None openTypeOS2SuperscriptXSize: Optional[int] = None openTypeOS2SuperscriptYSize: Optional[int] = None openTypeOS2SuperscriptXOffset: Optional[int] = None openTypeOS2SuperscriptYOffset: Optional[int] = None openTypeOS2StrikeoutSize: Optional[int] = None openTypeOS2StrikeoutPosition: Optional[int] = None openTypeVheaVertTypoAscender: Optional[int] = None openTypeVheaVertTypoDescender: Optional[int] = None openTypeVheaVertTypoLineGap: Optional[int] = None openTypeVheaCaretSlopeRise: Optional[int] = None openTypeVheaCaretSlopeRun: Optional[int] = None openTypeVheaCaretOffset: Optional[int] = None postscriptFontName: Optional[str] = None postscriptFullName: Optional[str] = None postscriptSlantAngle: Optional[float] = None postscriptUniqueID: Optional[int] = None postscriptUnderlineThickness: Optional[float] = None postscriptUnderlinePosition: Optional[float] = None postscriptIsFixedPitch: Optional[bool] = None postscriptBlueValues: Optional[List[float]] = None postscriptOtherBlues: Optional[List[float]] = None postscriptFamilyBlues: Optional[List[float]] = None postscriptFamilyOtherBlues: Optional[List[float]] = None postscriptStemSnapH: Optional[List[float]] = None postscriptStemSnapV: Optional[List[float]] = None postscriptBlueFuzz: Optional[float] = None postscriptBlueShift: Optional[float] = None postscriptBlueScale: Optional[float] = None postscriptForceBold: Optional[bool] = None postscriptDefaultWidthX: Optional[float] = None postscriptNominalWidthX: Optional[float] = None postscriptWeightName: Optional[str] = None postscriptDefaultCharacter: Optional[str] = None postscriptWindowsCharacterSet: Optional[str] = None # old stuff macintoshFONDName: Optional[str] = None macintoshFONDFamilyID: Optional[int] = None year: Optional[int] = None
[docs] @classmethod def read(cls, reader: UFOReader) -> "Info": """Instantiates a Info object from a :class:`fontTools.ufoLib.UFOReader`.""" self = cls() reader.readInfo(self) return self