datatypes.data_fontprops
1from typing import TYPE_CHECKING, List, Literal, Union 2import drawBot 3from dataclasses import dataclass, asdict 4from functools import cached_property 5 6from lib import helpers, fonts 7 8# Avoid circular import issues, hide from runtime, but keep for type checking 9if TYPE_CHECKING: 10 from datatypes import DFontList 11 from classes import KFont 12 13# TODO: Add RGB/CMYK fill property 14 15TrackingStrategy = Literal["relative", "absolute"] 16"""How letterSpacing input is interpreted: "relative" (default) scales by font size like Adobe tracking, "absolute" uses letterSpacing as absolute points like Sketch and CSS.""" 17 18 19# NamedTuple doesn’t allow custom init or internal properties 20# Dataclass to allow cached_property 21@dataclass 22class DFontProps: 23 """Data structure for font properties used in DrawBot. 24 25 Args: 26 fontSize: The font size in points. 27 leading: The leading (line spacing) multiplier. 28 letterSpacing: Tracking input value (relative by default, absolute if trackingStrategy="absolute"). 29 trackingStrategy: Tracking strategy: "relative" for Adobe-style, "absolute" for Sketch-style. 30 fontData: The font data or list of font data. 31 align: Text alignment option. 32 openType: OpenType font features. 33 """ 34 35 fontSize: int = 24 36 leading: float = 1.25 37 letterSpacing: int = 0 38 trackingStrategy: TrackingStrategy = "relative" 39 fontData: "KFont | DFontList | list[KFont]" = None 40 align: fonts.TextAlign = None 41 openType: fonts.FeatureId | list[fonts.FeatureId] | dict[fonts.FeatureId, bool] = ( 42 None 43 ) 44 45 @property 46 def lineHeight(self): 47 """The calculated line height. 48 49 Returns: 50 The product of fontSize and leading, or None if fontSize undefined. 51 """ 52 try: 53 return self.fontSize * self.leading 54 except Exception: 55 return 56 57 @cached_property 58 def blockHeight(self) -> float: 59 """The height of one line of text.""" 60 with drawBot.savedState(): 61 self.apply() 62 _, h = drawBot.textSize("H") 63 return h 64 65 @property 66 def tracking(self) -> float: 67 """Tracking value in DrawBot points. 68 69 Default behavior matches Adobe-style relative tracking where `letterSpacing` units scale by font size. Set `trackingStrategy="absolute"` to use Sketch-style absolute tracking in points. 70 """ 71 try: 72 if self.trackingStrategy == "absolute": 73 return self.letterSpacing 74 return self.letterSpacing * (self.fontSize / 1000) 75 except Exception: 76 return 0 77 78 @property 79 def fontPath(self) -> str: 80 """The file path of the font. 81 82 Returns: 83 The path to the font file, or the fontData if path is unavailable. 84 """ 85 try: 86 return self.fontData.path 87 except Exception: 88 return self.fontData 89 90 @property 91 def isSingleFont(self) -> bool: 92 """Whether fontData is a single font. 93 94 Returns: 95 True if fontData is a string or KFont instance, False otherwise. 96 """ 97 # Local import to avoid circular dependency at module level 98 from classes import KFont 99 100 return isinstance(self.fontData, (str, KFont)) 101 102 @property 103 def openTypeFeatures(self): 104 """OpenType features as a dictionary. 105 106 Returns: 107 Dictionary of OpenType features with bool values, or None if unavailable. 108 When openType is a dict it is returned as-is (supports explicit False). 109 When openType is a tag or list of tags every entry is set to True. 110 """ 111 if isinstance(self.openType, dict): 112 return dict(self.openType) 113 try: 114 return {key: True for key in helpers.coerceList(self.openType)} 115 except Exception: 116 return None 117 118 def getArgs(self, clean=False, pick: list[str] = None) -> dict: 119 """ 120 Get properties as a dictionary. 121 122 Args: 123 clean: If True, remove falsy properties. 124 pick: List of property keys to include. 125 126 Returns: 127 Dictionary of font properties. 128 """ 129 asDict = asdict(self) 130 asDict["lineHeight"] = self.lineHeight 131 asDict["tracking"] = self.tracking 132 asDict["openTypeFeatures"] = self.openTypeFeatures 133 134 if self.align: 135 asDict["align"] = self.align 136 137 if self.isSingleFont: 138 asDict["font"] = self.fontPath 139 140 if pick: 141 asDict = helpers.pick(asDict, pick) 142 143 if clean: 144 return helpers.omitBy(asDict) 145 else: 146 return asDict 147 148 def calc(self, clean=True) -> dict: 149 """ 150 Calculate keyword arguments for FormattedString(). 151 152 Args: 153 clean: If True, remove falsy properties. 154 155 Returns: 156 Dictionary of keyword arguments for FormattedString(). 157 """ 158 159 return self.getArgs( 160 clean=clean, 161 pick=[ 162 "font", 163 "fontSize", 164 "lineHeight", 165 "tracking", 166 "align", 167 "openTypeFeatures", 168 ], 169 ) 170 171 def activateFont(self): 172 """ 173 Set the font for DrawBot. 174 175 Returns: 176 The current instance. 177 """ 178 if self.isSingleFont: 179 drawBot.font(self.fontPath) 180 elif helpers.isSequence(self.fontData): 181 # If it's a list of fonts, activate the first one (for fallback) 182 drawBot.font(self.fontData[0].path) 183 184 return self 185 186 def unwrapFont(self) -> "KFont": 187 """Return a single KFont from fontData. 188 189 Raises: 190 ValueError: If fontData is None or empty. 191 TypeError: If the resolved item is not a KFont. 192 """ 193 from classes import KFont 194 195 fontItems = helpers.coerceList(self.fontData) 196 if not fontItems: 197 raise ValueError("fontData is empty; cannot unwrap a KFont.") 198 199 firstItem = fontItems[0] 200 if not isinstance(firstItem, KFont): 201 raise TypeError( 202 f"Expected KFont as first font item, got {type(firstItem).__name__}." 203 ) 204 205 return firstItem 206 207 def apply(self, activateFont: bool = True): 208 """ 209 Set DrawBot properties for the current font settings. 210 211 Args: 212 activateFont: Whether to activate the font (default True). 213 214 Returns: 215 The current instance. 216 """ 217 drawBot.fontSize(self.fontSize) 218 drawBot.lineHeight(self.lineHeight) 219 drawBot.tracking(self.tracking) 220 221 if activateFont: 222 self.activateFont() 223 224 if self.openTypeFeatures: 225 drawBot.openTypeFeatures(**self.openTypeFeatures) 226 else: 227 drawBot.openTypeFeatures(resetFeatures=True) 228 229 return self 230 231 def describe(self, format: Literal["string", "list"] = "string"): 232 """ 233 Return human-friendly font properties. 234 235 Args: 236 format: Return as 'str' or 'list[str]'. 237 238 Returns: 239 Human-readable font properties. 240 """ 241 242 props: List[float | int] = [self.fontSize] 243 lh, t = self.lineHeight, self.letterSpacing 244 245 if lh and lh != self.fontSize: 246 props.append(lh) 247 if t: 248 props.append(t) 249 250 rounded = [str(round(p)) for p in props] 251 return rounded if format == "list" else " ".join(rounded) 252 253 def describeFonts(self, includeFamily: bool = True) -> str: 254 """Human-friendly label for the font or font list.""" 255 fontItems: list[KFont] = helpers.coerceList(self.fontData) 256 fontExtremes: tuple[KFont] = helpers.pickExtremes(fontItems) 257 fontNames = [font.styleName for font in fontExtremes] 258 separator = "–" if len(fontItems) > 2 else ", " 259 260 styleNames = separator.join(fontNames) 261 if includeFamily: 262 familyName = fontItems[0].familyName 263 return f"{familyName} {styleNames}" 264 else: 265 return styleNames 266 267 def updateFont(self, value: Union["KFont", list["KFont"]]) -> "DFontProps": 268 """ 269 Update font and return self. 270 271 Args: 272 value: The new font or list of fonts. 273 274 Example: 275 `props.updateFont(newFont)` 276 """ 277 self.fontData = value 278 return self 279 280 def updateFontSize(self, value: int) -> "DFontProps": 281 """ 282 Update fontSize and return self. 283 284 Args: 285 value: The new font size. 286 287 Example: 288 `props.updateFontSize(12)` 289 """ 290 self.fontSize = value 291 return self 292 293 def makeParagraph( 294 self, 295 paragraphTopSpacing: float = 0, 296 paragraphBottomSpacing: float = 0, 297 indent: float = 0, 298 tailIndent: float = 0, 299 ): 300 """Convert to DParagraphProps with paragraph-level formatting. 301 302 Creates a new DParagraphProps instance with all current font properties 303 plus the specified paragraph formatting properties. 304 305 Args: 306 paragraphTopSpacing: Space before the paragraph in points. 307 paragraphBottomSpacing: Space after the paragraph in points. 308 indent: First-line indent in points. 309 tailIndent: Right margin indent in points. 310 311 Returns: 312 DParagraphProps instance with font and paragraph properties. 313 314 Example: 315 ```python 316 from datatypes import DFontProps 317 318 font_props = DFontProps(fontSize=16, leading=1.5) 319 para_props = font_props.makeParagraph(paragraphTopSpacing=20, indent=10) 320 321 # Chain with app helper: 322 props = app.getStyleProps('tiny').makeParagraph(paragraphTopSpacing=15) 323 ``` 324 """ 325 from .data_paragraphprops import DParagraphProps 326 327 return DParagraphProps( 328 fontSize=self.fontSize, 329 leading=self.leading, 330 letterSpacing=self.letterSpacing, 331 trackingStrategy=self.trackingStrategy, 332 fontData=self.fontData, 333 align=self.align, 334 openType=self.openType, 335 paragraphTopSpacing=paragraphTopSpacing, 336 paragraphBottomSpacing=paragraphBottomSpacing, 337 indent=indent, 338 tailIndent=tailIndent, 339 )
How letterSpacing input is interpreted: "relative" (default) scales by font size like Adobe tracking, "absolute" uses letterSpacing as absolute points like Sketch and CSS.
22@dataclass 23class DFontProps: 24 """Data structure for font properties used in DrawBot. 25 26 Args: 27 fontSize: The font size in points. 28 leading: The leading (line spacing) multiplier. 29 letterSpacing: Tracking input value (relative by default, absolute if trackingStrategy="absolute"). 30 trackingStrategy: Tracking strategy: "relative" for Adobe-style, "absolute" for Sketch-style. 31 fontData: The font data or list of font data. 32 align: Text alignment option. 33 openType: OpenType font features. 34 """ 35 36 fontSize: int = 24 37 leading: float = 1.25 38 letterSpacing: int = 0 39 trackingStrategy: TrackingStrategy = "relative" 40 fontData: "KFont | DFontList | list[KFont]" = None 41 align: fonts.TextAlign = None 42 openType: fonts.FeatureId | list[fonts.FeatureId] | dict[fonts.FeatureId, bool] = ( 43 None 44 ) 45 46 @property 47 def lineHeight(self): 48 """The calculated line height. 49 50 Returns: 51 The product of fontSize and leading, or None if fontSize undefined. 52 """ 53 try: 54 return self.fontSize * self.leading 55 except Exception: 56 return 57 58 @cached_property 59 def blockHeight(self) -> float: 60 """The height of one line of text.""" 61 with drawBot.savedState(): 62 self.apply() 63 _, h = drawBot.textSize("H") 64 return h 65 66 @property 67 def tracking(self) -> float: 68 """Tracking value in DrawBot points. 69 70 Default behavior matches Adobe-style relative tracking where `letterSpacing` units scale by font size. Set `trackingStrategy="absolute"` to use Sketch-style absolute tracking in points. 71 """ 72 try: 73 if self.trackingStrategy == "absolute": 74 return self.letterSpacing 75 return self.letterSpacing * (self.fontSize / 1000) 76 except Exception: 77 return 0 78 79 @property 80 def fontPath(self) -> str: 81 """The file path of the font. 82 83 Returns: 84 The path to the font file, or the fontData if path is unavailable. 85 """ 86 try: 87 return self.fontData.path 88 except Exception: 89 return self.fontData 90 91 @property 92 def isSingleFont(self) -> bool: 93 """Whether fontData is a single font. 94 95 Returns: 96 True if fontData is a string or KFont instance, False otherwise. 97 """ 98 # Local import to avoid circular dependency at module level 99 from classes import KFont 100 101 return isinstance(self.fontData, (str, KFont)) 102 103 @property 104 def openTypeFeatures(self): 105 """OpenType features as a dictionary. 106 107 Returns: 108 Dictionary of OpenType features with bool values, or None if unavailable. 109 When openType is a dict it is returned as-is (supports explicit False). 110 When openType is a tag or list of tags every entry is set to True. 111 """ 112 if isinstance(self.openType, dict): 113 return dict(self.openType) 114 try: 115 return {key: True for key in helpers.coerceList(self.openType)} 116 except Exception: 117 return None 118 119 def getArgs(self, clean=False, pick: list[str] = None) -> dict: 120 """ 121 Get properties as a dictionary. 122 123 Args: 124 clean: If True, remove falsy properties. 125 pick: List of property keys to include. 126 127 Returns: 128 Dictionary of font properties. 129 """ 130 asDict = asdict(self) 131 asDict["lineHeight"] = self.lineHeight 132 asDict["tracking"] = self.tracking 133 asDict["openTypeFeatures"] = self.openTypeFeatures 134 135 if self.align: 136 asDict["align"] = self.align 137 138 if self.isSingleFont: 139 asDict["font"] = self.fontPath 140 141 if pick: 142 asDict = helpers.pick(asDict, pick) 143 144 if clean: 145 return helpers.omitBy(asDict) 146 else: 147 return asDict 148 149 def calc(self, clean=True) -> dict: 150 """ 151 Calculate keyword arguments for FormattedString(). 152 153 Args: 154 clean: If True, remove falsy properties. 155 156 Returns: 157 Dictionary of keyword arguments for FormattedString(). 158 """ 159 160 return self.getArgs( 161 clean=clean, 162 pick=[ 163 "font", 164 "fontSize", 165 "lineHeight", 166 "tracking", 167 "align", 168 "openTypeFeatures", 169 ], 170 ) 171 172 def activateFont(self): 173 """ 174 Set the font for DrawBot. 175 176 Returns: 177 The current instance. 178 """ 179 if self.isSingleFont: 180 drawBot.font(self.fontPath) 181 elif helpers.isSequence(self.fontData): 182 # If it's a list of fonts, activate the first one (for fallback) 183 drawBot.font(self.fontData[0].path) 184 185 return self 186 187 def unwrapFont(self) -> "KFont": 188 """Return a single KFont from fontData. 189 190 Raises: 191 ValueError: If fontData is None or empty. 192 TypeError: If the resolved item is not a KFont. 193 """ 194 from classes import KFont 195 196 fontItems = helpers.coerceList(self.fontData) 197 if not fontItems: 198 raise ValueError("fontData is empty; cannot unwrap a KFont.") 199 200 firstItem = fontItems[0] 201 if not isinstance(firstItem, KFont): 202 raise TypeError( 203 f"Expected KFont as first font item, got {type(firstItem).__name__}." 204 ) 205 206 return firstItem 207 208 def apply(self, activateFont: bool = True): 209 """ 210 Set DrawBot properties for the current font settings. 211 212 Args: 213 activateFont: Whether to activate the font (default True). 214 215 Returns: 216 The current instance. 217 """ 218 drawBot.fontSize(self.fontSize) 219 drawBot.lineHeight(self.lineHeight) 220 drawBot.tracking(self.tracking) 221 222 if activateFont: 223 self.activateFont() 224 225 if self.openTypeFeatures: 226 drawBot.openTypeFeatures(**self.openTypeFeatures) 227 else: 228 drawBot.openTypeFeatures(resetFeatures=True) 229 230 return self 231 232 def describe(self, format: Literal["string", "list"] = "string"): 233 """ 234 Return human-friendly font properties. 235 236 Args: 237 format: Return as 'str' or 'list[str]'. 238 239 Returns: 240 Human-readable font properties. 241 """ 242 243 props: List[float | int] = [self.fontSize] 244 lh, t = self.lineHeight, self.letterSpacing 245 246 if lh and lh != self.fontSize: 247 props.append(lh) 248 if t: 249 props.append(t) 250 251 rounded = [str(round(p)) for p in props] 252 return rounded if format == "list" else " ".join(rounded) 253 254 def describeFonts(self, includeFamily: bool = True) -> str: 255 """Human-friendly label for the font or font list.""" 256 fontItems: list[KFont] = helpers.coerceList(self.fontData) 257 fontExtremes: tuple[KFont] = helpers.pickExtremes(fontItems) 258 fontNames = [font.styleName for font in fontExtremes] 259 separator = "–" if len(fontItems) > 2 else ", " 260 261 styleNames = separator.join(fontNames) 262 if includeFamily: 263 familyName = fontItems[0].familyName 264 return f"{familyName} {styleNames}" 265 else: 266 return styleNames 267 268 def updateFont(self, value: Union["KFont", list["KFont"]]) -> "DFontProps": 269 """ 270 Update font and return self. 271 272 Args: 273 value: The new font or list of fonts. 274 275 Example: 276 `props.updateFont(newFont)` 277 """ 278 self.fontData = value 279 return self 280 281 def updateFontSize(self, value: int) -> "DFontProps": 282 """ 283 Update fontSize and return self. 284 285 Args: 286 value: The new font size. 287 288 Example: 289 `props.updateFontSize(12)` 290 """ 291 self.fontSize = value 292 return self 293 294 def makeParagraph( 295 self, 296 paragraphTopSpacing: float = 0, 297 paragraphBottomSpacing: float = 0, 298 indent: float = 0, 299 tailIndent: float = 0, 300 ): 301 """Convert to DParagraphProps with paragraph-level formatting. 302 303 Creates a new DParagraphProps instance with all current font properties 304 plus the specified paragraph formatting properties. 305 306 Args: 307 paragraphTopSpacing: Space before the paragraph in points. 308 paragraphBottomSpacing: Space after the paragraph in points. 309 indent: First-line indent in points. 310 tailIndent: Right margin indent in points. 311 312 Returns: 313 DParagraphProps instance with font and paragraph properties. 314 315 Example: 316 ```python 317 from datatypes import DFontProps 318 319 font_props = DFontProps(fontSize=16, leading=1.5) 320 para_props = font_props.makeParagraph(paragraphTopSpacing=20, indent=10) 321 322 # Chain with app helper: 323 props = app.getStyleProps('tiny').makeParagraph(paragraphTopSpacing=15) 324 ``` 325 """ 326 from .data_paragraphprops import DParagraphProps 327 328 return DParagraphProps( 329 fontSize=self.fontSize, 330 leading=self.leading, 331 letterSpacing=self.letterSpacing, 332 trackingStrategy=self.trackingStrategy, 333 fontData=self.fontData, 334 align=self.align, 335 openType=self.openType, 336 paragraphTopSpacing=paragraphTopSpacing, 337 paragraphBottomSpacing=paragraphBottomSpacing, 338 indent=indent, 339 tailIndent=tailIndent, 340 )
Data structure for font properties used in DrawBot.
Arguments:
- fontSize: The font size in points.
- leading: The leading (line spacing) multiplier.
- letterSpacing: Tracking input value (relative by default, absolute if trackingStrategy="absolute").
- trackingStrategy: Tracking strategy: "relative" for Adobe-style, "absolute" for Sketch-style.
- fontData: The font data or list of font data.
- align: Text alignment option.
- openType: OpenType font features.
46 @property 47 def lineHeight(self): 48 """The calculated line height. 49 50 Returns: 51 The product of fontSize and leading, or None if fontSize undefined. 52 """ 53 try: 54 return self.fontSize * self.leading 55 except Exception: 56 return
The calculated line height.
Returns:
The product of fontSize and leading, or None if fontSize undefined.
58 @cached_property 59 def blockHeight(self) -> float: 60 """The height of one line of text.""" 61 with drawBot.savedState(): 62 self.apply() 63 _, h = drawBot.textSize("H") 64 return h
The height of one line of text.
66 @property 67 def tracking(self) -> float: 68 """Tracking value in DrawBot points. 69 70 Default behavior matches Adobe-style relative tracking where `letterSpacing` units scale by font size. Set `trackingStrategy="absolute"` to use Sketch-style absolute tracking in points. 71 """ 72 try: 73 if self.trackingStrategy == "absolute": 74 return self.letterSpacing 75 return self.letterSpacing * (self.fontSize / 1000) 76 except Exception: 77 return 0
Tracking value in DrawBot points.
Default behavior matches Adobe-style relative tracking where letterSpacing units scale by font size. Set trackingStrategy="absolute" to use Sketch-style absolute tracking in points.
79 @property 80 def fontPath(self) -> str: 81 """The file path of the font. 82 83 Returns: 84 The path to the font file, or the fontData if path is unavailable. 85 """ 86 try: 87 return self.fontData.path 88 except Exception: 89 return self.fontData
The file path of the font.
Returns:
The path to the font file, or the fontData if path is unavailable.
91 @property 92 def isSingleFont(self) -> bool: 93 """Whether fontData is a single font. 94 95 Returns: 96 True if fontData is a string or KFont instance, False otherwise. 97 """ 98 # Local import to avoid circular dependency at module level 99 from classes import KFont 100 101 return isinstance(self.fontData, (str, KFont))
Whether fontData is a single font.
Returns:
True if fontData is a string or KFont instance, False otherwise.
103 @property 104 def openTypeFeatures(self): 105 """OpenType features as a dictionary. 106 107 Returns: 108 Dictionary of OpenType features with bool values, or None if unavailable. 109 When openType is a dict it is returned as-is (supports explicit False). 110 When openType is a tag or list of tags every entry is set to True. 111 """ 112 if isinstance(self.openType, dict): 113 return dict(self.openType) 114 try: 115 return {key: True for key in helpers.coerceList(self.openType)} 116 except Exception: 117 return None
OpenType features as a dictionary.
Returns:
Dictionary of OpenType features with bool values, or None if unavailable. When openType is a dict it is returned as-is (supports explicit False). When openType is a tag or list of tags every entry is set to True.
119 def getArgs(self, clean=False, pick: list[str] = None) -> dict: 120 """ 121 Get properties as a dictionary. 122 123 Args: 124 clean: If True, remove falsy properties. 125 pick: List of property keys to include. 126 127 Returns: 128 Dictionary of font properties. 129 """ 130 asDict = asdict(self) 131 asDict["lineHeight"] = self.lineHeight 132 asDict["tracking"] = self.tracking 133 asDict["openTypeFeatures"] = self.openTypeFeatures 134 135 if self.align: 136 asDict["align"] = self.align 137 138 if self.isSingleFont: 139 asDict["font"] = self.fontPath 140 141 if pick: 142 asDict = helpers.pick(asDict, pick) 143 144 if clean: 145 return helpers.omitBy(asDict) 146 else: 147 return asDict
Get properties as a dictionary.
Arguments:
- clean: If True, remove falsy properties.
- pick: List of property keys to include.
Returns:
Dictionary of font properties.
149 def calc(self, clean=True) -> dict: 150 """ 151 Calculate keyword arguments for FormattedString(). 152 153 Args: 154 clean: If True, remove falsy properties. 155 156 Returns: 157 Dictionary of keyword arguments for FormattedString(). 158 """ 159 160 return self.getArgs( 161 clean=clean, 162 pick=[ 163 "font", 164 "fontSize", 165 "lineHeight", 166 "tracking", 167 "align", 168 "openTypeFeatures", 169 ], 170 )
Calculate keyword arguments for FormattedString().
Arguments:
- clean: If True, remove falsy properties.
Returns:
Dictionary of keyword arguments for FormattedString().
172 def activateFont(self): 173 """ 174 Set the font for DrawBot. 175 176 Returns: 177 The current instance. 178 """ 179 if self.isSingleFont: 180 drawBot.font(self.fontPath) 181 elif helpers.isSequence(self.fontData): 182 # If it's a list of fonts, activate the first one (for fallback) 183 drawBot.font(self.fontData[0].path) 184 185 return self
Set the font for DrawBot.
Returns:
The current instance.
187 def unwrapFont(self) -> "KFont": 188 """Return a single KFont from fontData. 189 190 Raises: 191 ValueError: If fontData is None or empty. 192 TypeError: If the resolved item is not a KFont. 193 """ 194 from classes import KFont 195 196 fontItems = helpers.coerceList(self.fontData) 197 if not fontItems: 198 raise ValueError("fontData is empty; cannot unwrap a KFont.") 199 200 firstItem = fontItems[0] 201 if not isinstance(firstItem, KFont): 202 raise TypeError( 203 f"Expected KFont as first font item, got {type(firstItem).__name__}." 204 ) 205 206 return firstItem
Return a single KFont from fontData.
Raises:
- ValueError: If fontData is None or empty.
- TypeError: If the resolved item is not a KFont.
208 def apply(self, activateFont: bool = True): 209 """ 210 Set DrawBot properties for the current font settings. 211 212 Args: 213 activateFont: Whether to activate the font (default True). 214 215 Returns: 216 The current instance. 217 """ 218 drawBot.fontSize(self.fontSize) 219 drawBot.lineHeight(self.lineHeight) 220 drawBot.tracking(self.tracking) 221 222 if activateFont: 223 self.activateFont() 224 225 if self.openTypeFeatures: 226 drawBot.openTypeFeatures(**self.openTypeFeatures) 227 else: 228 drawBot.openTypeFeatures(resetFeatures=True) 229 230 return self
Set DrawBot properties for the current font settings.
Arguments:
- activateFont: Whether to activate the font (default True).
Returns:
The current instance.
232 def describe(self, format: Literal["string", "list"] = "string"): 233 """ 234 Return human-friendly font properties. 235 236 Args: 237 format: Return as 'str' or 'list[str]'. 238 239 Returns: 240 Human-readable font properties. 241 """ 242 243 props: List[float | int] = [self.fontSize] 244 lh, t = self.lineHeight, self.letterSpacing 245 246 if lh and lh != self.fontSize: 247 props.append(lh) 248 if t: 249 props.append(t) 250 251 rounded = [str(round(p)) for p in props] 252 return rounded if format == "list" else " ".join(rounded)
Return human-friendly font properties.
Arguments:
- format: Return as 'str' or 'list[str]'.
Returns:
Human-readable font properties.
254 def describeFonts(self, includeFamily: bool = True) -> str: 255 """Human-friendly label for the font or font list.""" 256 fontItems: list[KFont] = helpers.coerceList(self.fontData) 257 fontExtremes: tuple[KFont] = helpers.pickExtremes(fontItems) 258 fontNames = [font.styleName for font in fontExtremes] 259 separator = "–" if len(fontItems) > 2 else ", " 260 261 styleNames = separator.join(fontNames) 262 if includeFamily: 263 familyName = fontItems[0].familyName 264 return f"{familyName} {styleNames}" 265 else: 266 return styleNames
Human-friendly label for the font or font list.
268 def updateFont(self, value: Union["KFont", list["KFont"]]) -> "DFontProps": 269 """ 270 Update font and return self. 271 272 Args: 273 value: The new font or list of fonts. 274 275 Example: 276 `props.updateFont(newFont)` 277 """ 278 self.fontData = value 279 return self
Update font and return self.
Arguments:
- value: The new font or list of fonts.
Example:
props.updateFont(newFont)
281 def updateFontSize(self, value: int) -> "DFontProps": 282 """ 283 Update fontSize and return self. 284 285 Args: 286 value: The new font size. 287 288 Example: 289 `props.updateFontSize(12)` 290 """ 291 self.fontSize = value 292 return self
Update fontSize and return self.
Arguments:
- value: The new font size.
Example:
props.updateFontSize(12)
294 def makeParagraph( 295 self, 296 paragraphTopSpacing: float = 0, 297 paragraphBottomSpacing: float = 0, 298 indent: float = 0, 299 tailIndent: float = 0, 300 ): 301 """Convert to DParagraphProps with paragraph-level formatting. 302 303 Creates a new DParagraphProps instance with all current font properties 304 plus the specified paragraph formatting properties. 305 306 Args: 307 paragraphTopSpacing: Space before the paragraph in points. 308 paragraphBottomSpacing: Space after the paragraph in points. 309 indent: First-line indent in points. 310 tailIndent: Right margin indent in points. 311 312 Returns: 313 DParagraphProps instance with font and paragraph properties. 314 315 Example: 316 ```python 317 from datatypes import DFontProps 318 319 font_props = DFontProps(fontSize=16, leading=1.5) 320 para_props = font_props.makeParagraph(paragraphTopSpacing=20, indent=10) 321 322 # Chain with app helper: 323 props = app.getStyleProps('tiny').makeParagraph(paragraphTopSpacing=15) 324 ``` 325 """ 326 from .data_paragraphprops import DParagraphProps 327 328 return DParagraphProps( 329 fontSize=self.fontSize, 330 leading=self.leading, 331 letterSpacing=self.letterSpacing, 332 trackingStrategy=self.trackingStrategy, 333 fontData=self.fontData, 334 align=self.align, 335 openType=self.openType, 336 paragraphTopSpacing=paragraphTopSpacing, 337 paragraphBottomSpacing=paragraphBottomSpacing, 338 indent=indent, 339 tailIndent=tailIndent, 340 )
Convert to DParagraphProps with paragraph-level formatting.
Creates a new DParagraphProps instance with all current font properties plus the specified paragraph formatting properties.
Arguments:
- paragraphTopSpacing: Space before the paragraph in points.
- paragraphBottomSpacing: Space after the paragraph in points.
- indent: First-line indent in points.
- tailIndent: Right margin indent in points.
Returns:
DParagraphProps instance with font and paragraph properties.
Example:
from datatypes import DFontProps font_props = DFontProps(fontSize=16, leading=1.5) para_props = font_props.makeParagraph(paragraphTopSpacing=20, indent=10) # Chain with app helper: props = app.getStyleProps('tiny').makeParagraph(paragraphTopSpacing=15)