datatypes.data_fontprops

  1from typing import List, Literal, NamedTuple
  2import drawBot
  3
  4from lib import helpers, fonts
  5from classes import KFont
  6
  7# TODO: Add RGB/CMYK fill property
  8
  9
 10class DFontProps(NamedTuple):
 11    """Data structure for font properties used in DrawBot."""
 12
 13    fontSize: int = 24
 14    """The font size in points."""
 15
 16    leading: float = 1.25
 17    """The leading (line spacing) multiplier."""
 18
 19    letterSpacing: int = 0
 20    """Relative letter spacing unit, behaves similar to Adobe."""
 21
 22    fontData: KFont | list[KFont] = None
 23    """The font data or list of font data."""
 24
 25    align: str = None
 26    """Text alignment option."""
 27
 28    openType: fonts.FontFeature | list[fonts.FontFeature] = None
 29    """OpenType font features."""
 30
 31    @property
 32    def lineHeight(self):
 33        """The calculated line height.
 34
 35        Returns:
 36            The product of fontSize and leading, or None if fontSize undefined.
 37        """
 38        try:
 39            return self.fontSize * self.leading
 40        except Exception:
 41            return
 42
 43    @property
 44    def tracking(self):
 45        """The absolute tracking value, scales with fontSize.
 46
 47        Returns:
 48            The calculated tracking value, or None if calculation fails.
 49        """
 50        try:
 51            return self.letterSpacing * (self.fontSize / 1000)
 52        except Exception:
 53            return
 54
 55    @property
 56    def fontPath(self):
 57        """The file path of the font.
 58
 59        Returns:
 60            The path to the font file, or the fontData if path is unavailable.
 61        """
 62        try:
 63            return self.fontData.path
 64        except Exception:
 65            return self.fontData
 66
 67    @property
 68    def isSingleFont(self) -> bool:
 69        """Whether fontData is a single font.
 70
 71        Returns:
 72            True if fontData is a string or KFont instance, False otherwise.
 73        """
 74        return isinstance(self.fontData, (str, KFont))
 75
 76    @property
 77    def openTypeFeatures(self):
 78        """OpenType features as a dictionary.
 79
 80        Returns:
 81            Dictionary of OpenType features set to True, or None if unavailable.
 82        """
 83        try:
 84            return {key: True for key in helpers.coerceList(self.openType)}
 85        except Exception:
 86            pass
 87
 88    def getArgs(self, clean=False, pick: list[str] = None) -> dict:
 89        """
 90        Get properties as a dictionary.
 91
 92        Args:
 93            clean: If True, remove falsy properties.
 94            pick: List of property keys to include.
 95
 96        Returns:
 97            Dictionary of font properties.
 98        """
 99        asDict = self._asdict()
100        asDict["lineHeight"] = self.lineHeight
101        asDict["tracking"] = self.tracking
102        asDict["openTypeFeatures"] = self.openTypeFeatures
103
104        if self.align:
105            asDict["align"] = self.align
106
107        if self.isSingleFont:
108            asDict["font"] = self.fontPath
109
110        if pick:
111            asDict = helpers.pick(asDict, pick)
112
113        if clean:
114            return helpers.omitBy(asDict)
115        else:
116            return asDict
117
118    def calc(self, clean=True) -> dict:
119        """
120        Calculate keyword arguments for FormattedString().
121
122        Args:
123            clean: If True, remove falsy properties.
124
125        Returns:
126            Dictionary of keyword arguments for FormattedString().
127        """
128
129        return self.getArgs(
130            clean=clean,
131            pick=[
132                "font",
133                "fontSize",
134                "lineHeight",
135                "tracking",
136                "align",
137                "openTypeFeatures",
138            ],
139        )
140
141    def activateFont(self):
142        """
143        Set the font for DrawBot.
144
145        Returns:
146            The current instance.
147        """
148        if self.isSingleFont:
149            drawBot.font(self.fontPath)
150        return self
151
152    def apply(self):
153        """
154        Set DrawBot properties for the current font settings.
155
156        Returns:
157            The current instance.
158        """
159        drawBot.fontSize(self.fontSize)
160        drawBot.lineHeight(self.lineHeight)
161        drawBot.tracking(self.tracking)
162
163        self.activateFont()
164
165        if self.openTypeFeatures:
166            drawBot.openTypeFeatures(**self.openTypeFeatures)
167        else:
168            drawBot.openTypeFeatures(resetFeatures=True)
169
170        return self
171
172    def describe(self, format: Literal["string", "list"] = "string"):
173        """
174        Return human-friendly font properties.
175
176        Args:
177            format: Return as 'str' or 'list[str]'.
178
179        Returns:
180            Human-readable font properties.
181        """
182
183        props: List[float | int] = [self.fontSize]
184        lh, t = self.lineHeight, self.letterSpacing
185
186        if lh and lh != self.fontSize:
187            props.append(lh)
188        if t:
189            props.append(t)
190
191        rounded = [str(round(p)) for p in props]
192        return rounded if format == "list" else " ".join(rounded)
193
194    def updateFont(self, value: KFont):
195        """
196        Update font and return a new instance (since NamedTuple is immutable).
197
198        Args:
199            value: The new font.
200
201        Example:
202            `props = props.updateFont(newFont)`
203        """
204        return self._replace(fontData=value)
205
206    def updateFontSize(self, value: int):
207        """
208        Update fontSize and return a new instance (since NamedTuple is immutable).
209
210        Args:
211            value: The new font size.
212
213        Example:
214            `props = props.updateFontSize(12)`
215
216        Returns:
217            New instance with updated fontSize.
218        """
219        return self._replace(fontSize=value)
class DFontProps(typing.NamedTuple):
 11class DFontProps(NamedTuple):
 12    """Data structure for font properties used in DrawBot."""
 13
 14    fontSize: int = 24
 15    """The font size in points."""
 16
 17    leading: float = 1.25
 18    """The leading (line spacing) multiplier."""
 19
 20    letterSpacing: int = 0
 21    """Relative letter spacing unit, behaves similar to Adobe."""
 22
 23    fontData: KFont | list[KFont] = None
 24    """The font data or list of font data."""
 25
 26    align: str = None
 27    """Text alignment option."""
 28
 29    openType: fonts.FontFeature | list[fonts.FontFeature] = None
 30    """OpenType font features."""
 31
 32    @property
 33    def lineHeight(self):
 34        """The calculated line height.
 35
 36        Returns:
 37            The product of fontSize and leading, or None if fontSize undefined.
 38        """
 39        try:
 40            return self.fontSize * self.leading
 41        except Exception:
 42            return
 43
 44    @property
 45    def tracking(self):
 46        """The absolute tracking value, scales with fontSize.
 47
 48        Returns:
 49            The calculated tracking value, or None if calculation fails.
 50        """
 51        try:
 52            return self.letterSpacing * (self.fontSize / 1000)
 53        except Exception:
 54            return
 55
 56    @property
 57    def fontPath(self):
 58        """The file path of the font.
 59
 60        Returns:
 61            The path to the font file, or the fontData if path is unavailable.
 62        """
 63        try:
 64            return self.fontData.path
 65        except Exception:
 66            return self.fontData
 67
 68    @property
 69    def isSingleFont(self) -> bool:
 70        """Whether fontData is a single font.
 71
 72        Returns:
 73            True if fontData is a string or KFont instance, False otherwise.
 74        """
 75        return isinstance(self.fontData, (str, KFont))
 76
 77    @property
 78    def openTypeFeatures(self):
 79        """OpenType features as a dictionary.
 80
 81        Returns:
 82            Dictionary of OpenType features set to True, or None if unavailable.
 83        """
 84        try:
 85            return {key: True for key in helpers.coerceList(self.openType)}
 86        except Exception:
 87            pass
 88
 89    def getArgs(self, clean=False, pick: list[str] = None) -> dict:
 90        """
 91        Get properties as a dictionary.
 92
 93        Args:
 94            clean: If True, remove falsy properties.
 95            pick: List of property keys to include.
 96
 97        Returns:
 98            Dictionary of font properties.
 99        """
100        asDict = self._asdict()
101        asDict["lineHeight"] = self.lineHeight
102        asDict["tracking"] = self.tracking
103        asDict["openTypeFeatures"] = self.openTypeFeatures
104
105        if self.align:
106            asDict["align"] = self.align
107
108        if self.isSingleFont:
109            asDict["font"] = self.fontPath
110
111        if pick:
112            asDict = helpers.pick(asDict, pick)
113
114        if clean:
115            return helpers.omitBy(asDict)
116        else:
117            return asDict
118
119    def calc(self, clean=True) -> dict:
120        """
121        Calculate keyword arguments for FormattedString().
122
123        Args:
124            clean: If True, remove falsy properties.
125
126        Returns:
127            Dictionary of keyword arguments for FormattedString().
128        """
129
130        return self.getArgs(
131            clean=clean,
132            pick=[
133                "font",
134                "fontSize",
135                "lineHeight",
136                "tracking",
137                "align",
138                "openTypeFeatures",
139            ],
140        )
141
142    def activateFont(self):
143        """
144        Set the font for DrawBot.
145
146        Returns:
147            The current instance.
148        """
149        if self.isSingleFont:
150            drawBot.font(self.fontPath)
151        return self
152
153    def apply(self):
154        """
155        Set DrawBot properties for the current font settings.
156
157        Returns:
158            The current instance.
159        """
160        drawBot.fontSize(self.fontSize)
161        drawBot.lineHeight(self.lineHeight)
162        drawBot.tracking(self.tracking)
163
164        self.activateFont()
165
166        if self.openTypeFeatures:
167            drawBot.openTypeFeatures(**self.openTypeFeatures)
168        else:
169            drawBot.openTypeFeatures(resetFeatures=True)
170
171        return self
172
173    def describe(self, format: Literal["string", "list"] = "string"):
174        """
175        Return human-friendly font properties.
176
177        Args:
178            format: Return as 'str' or 'list[str]'.
179
180        Returns:
181            Human-readable font properties.
182        """
183
184        props: List[float | int] = [self.fontSize]
185        lh, t = self.lineHeight, self.letterSpacing
186
187        if lh and lh != self.fontSize:
188            props.append(lh)
189        if t:
190            props.append(t)
191
192        rounded = [str(round(p)) for p in props]
193        return rounded if format == "list" else " ".join(rounded)
194
195    def updateFont(self, value: KFont):
196        """
197        Update font and return a new instance (since NamedTuple is immutable).
198
199        Args:
200            value: The new font.
201
202        Example:
203            `props = props.updateFont(newFont)`
204        """
205        return self._replace(fontData=value)
206
207    def updateFontSize(self, value: int):
208        """
209        Update fontSize and return a new instance (since NamedTuple is immutable).
210
211        Args:
212            value: The new font size.
213
214        Example:
215            `props = props.updateFontSize(12)`
216
217        Returns:
218            New instance with updated fontSize.
219        """
220        return self._replace(fontSize=value)

Data structure for font properties used in DrawBot.

DFontProps( fontSize: int = 24, leading: float = 1.25, letterSpacing: int = 0, fontData: classes.c10_font.KFont | list[classes.c10_font.KFont] = None, align: str = None, openType: Union[Literal['calt', 'case', 'dlig', 'frac', 'liga', 'kern', 'lnum', 'onum', 'ordn', 'pnum', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09', 'ss10', 'subs', 'sups', 'titl', 'tnum'], list[Literal['calt', 'case', 'dlig', 'frac', 'liga', 'kern', 'lnum', 'onum', 'ordn', 'pnum', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09', 'ss10', 'subs', 'sups', 'titl', 'tnum']]] = None)

Create new instance of DFontProps(fontSize, leading, letterSpacing, fontData, align, openType)

fontSize: int

The font size in points.

leading: float

The leading (line spacing) multiplier.

letterSpacing: int

Relative letter spacing unit, behaves similar to Adobe.

The font data or list of font data.

align: str

Text alignment option.

openType: Union[Literal['calt', 'case', 'dlig', 'frac', 'liga', 'kern', 'lnum', 'onum', 'ordn', 'pnum', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09', 'ss10', 'subs', 'sups', 'titl', 'tnum'], list[Literal['calt', 'case', 'dlig', 'frac', 'liga', 'kern', 'lnum', 'onum', 'ordn', 'pnum', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09', 'ss10', 'subs', 'sups', 'titl', 'tnum']]]

OpenType font features.

lineHeight
32    @property
33    def lineHeight(self):
34        """The calculated line height.
35
36        Returns:
37            The product of fontSize and leading, or None if fontSize undefined.
38        """
39        try:
40            return self.fontSize * self.leading
41        except Exception:
42            return

The calculated line height.

Returns:

The product of fontSize and leading, or None if fontSize undefined.

tracking
44    @property
45    def tracking(self):
46        """The absolute tracking value, scales with fontSize.
47
48        Returns:
49            The calculated tracking value, or None if calculation fails.
50        """
51        try:
52            return self.letterSpacing * (self.fontSize / 1000)
53        except Exception:
54            return

The absolute tracking value, scales with fontSize.

Returns:

The calculated tracking value, or None if calculation fails.

fontPath
56    @property
57    def fontPath(self):
58        """The file path of the font.
59
60        Returns:
61            The path to the font file, or the fontData if path is unavailable.
62        """
63        try:
64            return self.fontData.path
65        except Exception:
66            return self.fontData

The file path of the font.

Returns:

The path to the font file, or the fontData if path is unavailable.

isSingleFont: bool
68    @property
69    def isSingleFont(self) -> bool:
70        """Whether fontData is a single font.
71
72        Returns:
73            True if fontData is a string or KFont instance, False otherwise.
74        """
75        return isinstance(self.fontData, (str, KFont))

Whether fontData is a single font.

Returns:

True if fontData is a string or KFont instance, False otherwise.

openTypeFeatures
77    @property
78    def openTypeFeatures(self):
79        """OpenType features as a dictionary.
80
81        Returns:
82            Dictionary of OpenType features set to True, or None if unavailable.
83        """
84        try:
85            return {key: True for key in helpers.coerceList(self.openType)}
86        except Exception:
87            pass

OpenType features as a dictionary.

Returns:

Dictionary of OpenType features set to True, or None if unavailable.

def getArgs(self, clean=False, pick: list[str] = None) -> dict:
 89    def getArgs(self, clean=False, pick: list[str] = None) -> dict:
 90        """
 91        Get properties as a dictionary.
 92
 93        Args:
 94            clean: If True, remove falsy properties.
 95            pick: List of property keys to include.
 96
 97        Returns:
 98            Dictionary of font properties.
 99        """
100        asDict = self._asdict()
101        asDict["lineHeight"] = self.lineHeight
102        asDict["tracking"] = self.tracking
103        asDict["openTypeFeatures"] = self.openTypeFeatures
104
105        if self.align:
106            asDict["align"] = self.align
107
108        if self.isSingleFont:
109            asDict["font"] = self.fontPath
110
111        if pick:
112            asDict = helpers.pick(asDict, pick)
113
114        if clean:
115            return helpers.omitBy(asDict)
116        else:
117            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.

def calc(self, clean=True) -> dict:
119    def calc(self, clean=True) -> dict:
120        """
121        Calculate keyword arguments for FormattedString().
122
123        Args:
124            clean: If True, remove falsy properties.
125
126        Returns:
127            Dictionary of keyword arguments for FormattedString().
128        """
129
130        return self.getArgs(
131            clean=clean,
132            pick=[
133                "font",
134                "fontSize",
135                "lineHeight",
136                "tracking",
137                "align",
138                "openTypeFeatures",
139            ],
140        )

Calculate keyword arguments for FormattedString().

Arguments:
  • clean: If True, remove falsy properties.
Returns:

Dictionary of keyword arguments for FormattedString().

def activateFont(self):
142    def activateFont(self):
143        """
144        Set the font for DrawBot.
145
146        Returns:
147            The current instance.
148        """
149        if self.isSingleFont:
150            drawBot.font(self.fontPath)
151        return self

Set the font for DrawBot.

Returns:

The current instance.

def apply(self):
153    def apply(self):
154        """
155        Set DrawBot properties for the current font settings.
156
157        Returns:
158            The current instance.
159        """
160        drawBot.fontSize(self.fontSize)
161        drawBot.lineHeight(self.lineHeight)
162        drawBot.tracking(self.tracking)
163
164        self.activateFont()
165
166        if self.openTypeFeatures:
167            drawBot.openTypeFeatures(**self.openTypeFeatures)
168        else:
169            drawBot.openTypeFeatures(resetFeatures=True)
170
171        return self

Set DrawBot properties for the current font settings.

Returns:

The current instance.

def describe(self, format: Literal['string', 'list'] = 'string'):
173    def describe(self, format: Literal["string", "list"] = "string"):
174        """
175        Return human-friendly font properties.
176
177        Args:
178            format: Return as 'str' or 'list[str]'.
179
180        Returns:
181            Human-readable font properties.
182        """
183
184        props: List[float | int] = [self.fontSize]
185        lh, t = self.lineHeight, self.letterSpacing
186
187        if lh and lh != self.fontSize:
188            props.append(lh)
189        if t:
190            props.append(t)
191
192        rounded = [str(round(p)) for p in props]
193        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.

def updateFont(self, value: classes.c10_font.KFont):
195    def updateFont(self, value: KFont):
196        """
197        Update font and return a new instance (since NamedTuple is immutable).
198
199        Args:
200            value: The new font.
201
202        Example:
203            `props = props.updateFont(newFont)`
204        """
205        return self._replace(fontData=value)

Update font and return a new instance (since NamedTuple is immutable).

Arguments:
  • value: The new font.
Example:

props = props.updateFont(newFont)

def updateFontSize(self, value: int):
207    def updateFontSize(self, value: int):
208        """
209        Update fontSize and return a new instance (since NamedTuple is immutable).
210
211        Args:
212            value: The new font size.
213
214        Example:
215            `props = props.updateFontSize(12)`
216
217        Returns:
218            New instance with updated fontSize.
219        """
220        return self._replace(fontSize=value)

Update fontSize and return a new instance (since NamedTuple is immutable).

Arguments:
  • value: The new font size.
Example:

props = props.updateFontSize(12)

Returns:

New instance with updated fontSize.