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.