datatypes.data_fontlist

  1from collections import UserList
  2from typing import TYPE_CHECKING, Literal, TypeAlias
  3
  4if TYPE_CHECKING:
  5    from classes import KFont
  6
  7
  8SortWeightOrder: TypeAlias = Literal["heaviest-first", "lightest-first"]
  9SortSlopeOrder: TypeAlias = Literal["upright-first", "italic-first"]
 10
 11
 12class DFontList(UserList["KFont"]):
 13    """List-like container for KFont items with ergonomic weight sorting."""
 14
 15    def __repr__(self) -> str:
 16        return f"DFontList({self.data!r})"
 17
 18    @staticmethod
 19    def _validateWeightOrder(weightOrder: SortWeightOrder) -> None:
 20        validOrders = ["heaviest-first", "lightest-first"]
 21        if weightOrder not in validOrders:
 22            raise ValueError(
 23                f"Invalid weight order: {weightOrder!r}. Use one of {validOrders}."
 24            )
 25
 26    @staticmethod
 27    def _validateSlopeOrder(slopeOrder: SortSlopeOrder) -> None:
 28        validOrders = ["upright-first", "italic-first"]
 29        if slopeOrder not in validOrders:
 30            raise ValueError(
 31                f"Invalid slope order: {slopeOrder!r}. Use one of {validOrders}."
 32            )
 33
 34    @staticmethod
 35    def _weight(font: "KFont") -> int:
 36        try:
 37            value = font.getWeightClass(numeric=True)
 38            if not isinstance(value, int):
 39                raise TypeError()
 40            return value
 41        except Exception as exc:
 42            raise ValueError(
 43                f"Cannot sort font without numeric weight class: {font}"
 44            ) from exc
 45
 46    @staticmethod
 47    def _slopeRank(font: "KFont", slopeOrder: SortSlopeOrder) -> int:
 48        isUpright = font.isType("upright")
 49        isItalic = font.isType("italic")
 50
 51        if isUpright:
 52            return 0 if slopeOrder == "upright-first" else 1
 53        if isItalic:
 54            return 1 if slopeOrder == "upright-first" else 0
 55
 56        return 2
 57
 58    @classmethod
 59    def _sortKey(
 60        cls,
 61        font: "KFont",
 62        weightOrder: SortWeightOrder,
 63        slopeOrder: SortSlopeOrder,
 64    ) -> tuple[int, int]:
 65        weight = cls._weight(font)
 66        if weightOrder == "heaviest-first":
 67            weight = -weight
 68        return weight, cls._slopeRank(font, slopeOrder)
 69
 70    def sortStyles(
 71        self,
 72        weightOrder: SortWeightOrder = "lightest-first",
 73        slopeOrder: SortSlopeOrder = "upright-first",
 74    ) -> "DFontList":
 75        """
 76        Sort list in-place by weight class and slope.
 77
 78        - Weight order is controlled by `weightOrder`.
 79        - Slope order is controlled by `slopeOrder`.
 80
 81        Returns:
 82                The same DFontList instance for chaining.
 83        """
 84        self._validateWeightOrder(weightOrder)
 85        self._validateSlopeOrder(slopeOrder)
 86        self.data.sort(key=lambda font: self._sortKey(font, weightOrder, slopeOrder))
 87        return self
 88
 89    def groupBySlope(
 90        self, weightOrder: SortWeightOrder = "lightest-first"
 91    ) -> tuple["DFontList", "DFontList"]:
 92        """
 93        Group fonts by slope into upright and italic collections, sorted by weight.
 94
 95        Args:
 96                weightOrder: Weight ordering inside each slope group.
 97
 98        Returns:
 99                Tuple of `(uprightGroup, italicGroup)`, each as a DFontList.
100        """
101        sortedFonts = DFontList(self.data.copy()).sortStyles(weightOrder)
102        uprightGroup = DFontList(
103            [font for font in sortedFonts if font.isType("upright")]
104        )
105        italicGroup = DFontList([font for font in sortedFonts if font.isType("italic")])
106        return uprightGroup, italicGroup
107
108    def groupByWeight(
109        self,
110        weightOrder: SortWeightOrder = "lightest-first",
111        slopeOrder: SortSlopeOrder = "upright-first",
112    ) -> dict[int, "DFontList"]:
113        """
114        Group fonts by numeric weight class.
115
116        Args:
117                weightOrder: Weight ordering inside each weight group.
118                slopeOrder: Slope ordering inside each weight group.
119
120        Returns:
121                Dictionary mapping numeric weight class to DFontList of fonts.
122        """
123        self._validateWeightOrder(weightOrder)
124        self._validateSlopeOrder(slopeOrder)
125        groups: dict[int, DFontList] = {}
126        for font in self.data:
127            weight = self._weight(font)
128            if weight not in groups:
129                groups[weight] = DFontList()
130            groups[weight].append(font)
131        for group in groups.values():
132            group.sortStyles(weightOrder=weightOrder, slopeOrder=slopeOrder)
133
134        orderedWeights = sorted(groups.keys(), reverse=weightOrder == "heaviest-first")
135        return {weight: groups[weight] for weight in orderedWeights}
SortWeightOrder: TypeAlias = Literal['heaviest-first', 'lightest-first']
SortSlopeOrder: TypeAlias = Literal['upright-first', 'italic-first']
class DFontList(collections.UserList['KFont']):
 13class DFontList(UserList["KFont"]):
 14    """List-like container for KFont items with ergonomic weight sorting."""
 15
 16    def __repr__(self) -> str:
 17        return f"DFontList({self.data!r})"
 18
 19    @staticmethod
 20    def _validateWeightOrder(weightOrder: SortWeightOrder) -> None:
 21        validOrders = ["heaviest-first", "lightest-first"]
 22        if weightOrder not in validOrders:
 23            raise ValueError(
 24                f"Invalid weight order: {weightOrder!r}. Use one of {validOrders}."
 25            )
 26
 27    @staticmethod
 28    def _validateSlopeOrder(slopeOrder: SortSlopeOrder) -> None:
 29        validOrders = ["upright-first", "italic-first"]
 30        if slopeOrder not in validOrders:
 31            raise ValueError(
 32                f"Invalid slope order: {slopeOrder!r}. Use one of {validOrders}."
 33            )
 34
 35    @staticmethod
 36    def _weight(font: "KFont") -> int:
 37        try:
 38            value = font.getWeightClass(numeric=True)
 39            if not isinstance(value, int):
 40                raise TypeError()
 41            return value
 42        except Exception as exc:
 43            raise ValueError(
 44                f"Cannot sort font without numeric weight class: {font}"
 45            ) from exc
 46
 47    @staticmethod
 48    def _slopeRank(font: "KFont", slopeOrder: SortSlopeOrder) -> int:
 49        isUpright = font.isType("upright")
 50        isItalic = font.isType("italic")
 51
 52        if isUpright:
 53            return 0 if slopeOrder == "upright-first" else 1
 54        if isItalic:
 55            return 1 if slopeOrder == "upright-first" else 0
 56
 57        return 2
 58
 59    @classmethod
 60    def _sortKey(
 61        cls,
 62        font: "KFont",
 63        weightOrder: SortWeightOrder,
 64        slopeOrder: SortSlopeOrder,
 65    ) -> tuple[int, int]:
 66        weight = cls._weight(font)
 67        if weightOrder == "heaviest-first":
 68            weight = -weight
 69        return weight, cls._slopeRank(font, slopeOrder)
 70
 71    def sortStyles(
 72        self,
 73        weightOrder: SortWeightOrder = "lightest-first",
 74        slopeOrder: SortSlopeOrder = "upright-first",
 75    ) -> "DFontList":
 76        """
 77        Sort list in-place by weight class and slope.
 78
 79        - Weight order is controlled by `weightOrder`.
 80        - Slope order is controlled by `slopeOrder`.
 81
 82        Returns:
 83                The same DFontList instance for chaining.
 84        """
 85        self._validateWeightOrder(weightOrder)
 86        self._validateSlopeOrder(slopeOrder)
 87        self.data.sort(key=lambda font: self._sortKey(font, weightOrder, slopeOrder))
 88        return self
 89
 90    def groupBySlope(
 91        self, weightOrder: SortWeightOrder = "lightest-first"
 92    ) -> tuple["DFontList", "DFontList"]:
 93        """
 94        Group fonts by slope into upright and italic collections, sorted by weight.
 95
 96        Args:
 97                weightOrder: Weight ordering inside each slope group.
 98
 99        Returns:
100                Tuple of `(uprightGroup, italicGroup)`, each as a DFontList.
101        """
102        sortedFonts = DFontList(self.data.copy()).sortStyles(weightOrder)
103        uprightGroup = DFontList(
104            [font for font in sortedFonts if font.isType("upright")]
105        )
106        italicGroup = DFontList([font for font in sortedFonts if font.isType("italic")])
107        return uprightGroup, italicGroup
108
109    def groupByWeight(
110        self,
111        weightOrder: SortWeightOrder = "lightest-first",
112        slopeOrder: SortSlopeOrder = "upright-first",
113    ) -> dict[int, "DFontList"]:
114        """
115        Group fonts by numeric weight class.
116
117        Args:
118                weightOrder: Weight ordering inside each weight group.
119                slopeOrder: Slope ordering inside each weight group.
120
121        Returns:
122                Dictionary mapping numeric weight class to DFontList of fonts.
123        """
124        self._validateWeightOrder(weightOrder)
125        self._validateSlopeOrder(slopeOrder)
126        groups: dict[int, DFontList] = {}
127        for font in self.data:
128            weight = self._weight(font)
129            if weight not in groups:
130                groups[weight] = DFontList()
131            groups[weight].append(font)
132        for group in groups.values():
133            group.sortStyles(weightOrder=weightOrder, slopeOrder=slopeOrder)
134
135        orderedWeights = sorted(groups.keys(), reverse=weightOrder == "heaviest-first")
136        return {weight: groups[weight] for weight in orderedWeights}

List-like container for KFont items with ergonomic weight sorting.

def sortStyles( self, weightOrder: Literal['heaviest-first', 'lightest-first'] = 'lightest-first', slopeOrder: Literal['upright-first', 'italic-first'] = 'upright-first') -> DFontList:
71    def sortStyles(
72        self,
73        weightOrder: SortWeightOrder = "lightest-first",
74        slopeOrder: SortSlopeOrder = "upright-first",
75    ) -> "DFontList":
76        """
77        Sort list in-place by weight class and slope.
78
79        - Weight order is controlled by `weightOrder`.
80        - Slope order is controlled by `slopeOrder`.
81
82        Returns:
83                The same DFontList instance for chaining.
84        """
85        self._validateWeightOrder(weightOrder)
86        self._validateSlopeOrder(slopeOrder)
87        self.data.sort(key=lambda font: self._sortKey(font, weightOrder, slopeOrder))
88        return self

Sort list in-place by weight class and slope.

  • Weight order is controlled by weightOrder.
  • Slope order is controlled by slopeOrder.
Returns:

The same DFontList instance for chaining.

def groupBySlope( self, weightOrder: Literal['heaviest-first', 'lightest-first'] = 'lightest-first') -> tuple[DFontList, DFontList]:
 90    def groupBySlope(
 91        self, weightOrder: SortWeightOrder = "lightest-first"
 92    ) -> tuple["DFontList", "DFontList"]:
 93        """
 94        Group fonts by slope into upright and italic collections, sorted by weight.
 95
 96        Args:
 97                weightOrder: Weight ordering inside each slope group.
 98
 99        Returns:
100                Tuple of `(uprightGroup, italicGroup)`, each as a DFontList.
101        """
102        sortedFonts = DFontList(self.data.copy()).sortStyles(weightOrder)
103        uprightGroup = DFontList(
104            [font for font in sortedFonts if font.isType("upright")]
105        )
106        italicGroup = DFontList([font for font in sortedFonts if font.isType("italic")])
107        return uprightGroup, italicGroup

Group fonts by slope into upright and italic collections, sorted by weight.

Arguments:
  • weightOrder: Weight ordering inside each slope group.
Returns:

Tuple of (uprightGroup, italicGroup), each as a DFontList.

def groupByWeight( self, weightOrder: Literal['heaviest-first', 'lightest-first'] = 'lightest-first', slopeOrder: Literal['upright-first', 'italic-first'] = 'upright-first') -> dict[int, DFontList]:
109    def groupByWeight(
110        self,
111        weightOrder: SortWeightOrder = "lightest-first",
112        slopeOrder: SortSlopeOrder = "upright-first",
113    ) -> dict[int, "DFontList"]:
114        """
115        Group fonts by numeric weight class.
116
117        Args:
118                weightOrder: Weight ordering inside each weight group.
119                slopeOrder: Slope ordering inside each weight group.
120
121        Returns:
122                Dictionary mapping numeric weight class to DFontList of fonts.
123        """
124        self._validateWeightOrder(weightOrder)
125        self._validateSlopeOrder(slopeOrder)
126        groups: dict[int, DFontList] = {}
127        for font in self.data:
128            weight = self._weight(font)
129            if weight not in groups:
130                groups[weight] = DFontList()
131            groups[weight].append(font)
132        for group in groups.values():
133            group.sortStyles(weightOrder=weightOrder, slopeOrder=slopeOrder)
134
135        orderedWeights = sorted(groups.keys(), reverse=weightOrder == "heaviest-first")
136        return {weight: groups[weight] for weight in orderedWeights}

Group fonts by numeric weight class.

Arguments:
  • weightOrder: Weight ordering inside each weight group.
  • slopeOrder: Slope ordering inside each weight group.
Returns:

Dictionary mapping numeric weight class to DFontList of fonts.