classes.c40_text_composer

  1from typing import TYPE_CHECKING, Literal
  2import drawBot
  3import copy
  4
  5from lib import content, fonts
  6from .c20_box import KBox
  7from .c32_pool import KPool
  8
  9# Avoid circular import issues, hide from runtime, but keep for type checking
 10if TYPE_CHECKING:
 11    from datatypes import DFontProps
 12
 13
 14class KTextComposer:
 15    """Composes and manages formatted text within a given frame."""
 16
 17    def __init__(
 18        self,
 19        frame: KBox,
 20        fontProps: "DFontProps",
 21        pool: KPool,
 22        formattedArgs: dict = None,
 23    ):
 24        """
 25        Initializes the text composer with a frame, font properties, and a word pool.
 26
 27        Args:
 28            frame: The frame within which the text will be composed.
 29            fontProps: The font properties to be applied to the text.
 30            pool: The word pool to be used for text composition.
 31            formattedArgs: Additional arguments for formatting the text.
 32        """
 33        self.frame = frame
 34        self.fontProps = fontProps.apply()
 35        self.formattedArgs = formattedArgs if formattedArgs else dict()
 36        self.fString = drawBot.FormattedString(
 37            **self.fontProps.calc(), **self.formattedArgs
 38        )
 39        self.pool = pool
 40
 41    @property
 42    def height(self) -> float:
 43        """
 44        Calculates the height of the composed text.
 45
 46        Returns:
 47            The height of the text in points.
 48        """
 49        return drawBot.textSize(self.fString, width=self.frame.width)[1]
 50
 51    def setPool(self, pool: KPool):
 52        """
 53        Sets the word pool for the composer.
 54        """
 55        self.pool = pool
 56        return self
 57
 58    def setOpenTypeFeatures(self, **features: dict[fonts.FeatureId, bool]):
 59        """
 60        Sets OpenType features for the formatted string.
 61        """
 62        drawBot.openTypeFeatures(**features)
 63        self.fString.openTypeFeatures(**features)
 64        return self
 65
 66    def addWord(
 67        self,
 68        width: float,
 69        shape: str = None,
 70        tokens: list[content.CharacterToken] = None,
 71    ):
 72        """
 73        Adds a word from the pool that fits the specified width and optional filters.
 74        """
 75        if not self.pool:
 76            raise AttributeError("No pool set. Use setPool() to set a pool.")
 77
 78        linePool = copy.copy(self.pool)  # ? Create a pool for this line
 79
 80        if shape:
 81            linePool = linePool.filterWithRegex(shape)
 82
 83        if tokens:
 84            linePool = linePool.filterTokens(tokens)
 85
 86        word = linePool.getItemByWidth(self.frame.width * width)
 87        # Skip removing from the main pool: Commit June 14, 2025
 88        self._append(word)
 89        return self
 90
 91    def addString(self, string: str):
 92        """
 93        Adds a raw string to the formatted text.
 94        """
 95        self._append(string)
 96        return self
 97
 98    def _append(self, string: str):
 99        """
100        Helper method to append text to the formatted string, ensuring that formatting is preserved.
101        """
102        # ? Reapply formatted args: spacing needs to be explicit in case it changed
103        self.fString.append(string, **self.fontProps.calc(), **self.formattedArgs)
104
105    def breakLine(self):
106        """
107        Inserts a line break in the formatted text.
108        """
109        self.fString.append("\n")
110        return self
111
112    def setTab(self, width: float, alignment: fonts.TextAlign):
113        """
114        Sets a tab stop at the given width and alignment, then inserts a tab.
115        """
116        self.fString.tabs((self.frame.width * width, alignment))
117        self.fString.append("\t")
118        return self
119
120    def fitForString(self, string: str):
121        """
122        Adjusts font size to fit the string on a single line within the frame. Updates `fontProps` as a side effect.
123        """
124        fs, *_ = fonts.fitBinary(
125            string,
126            self.frame.coords,
127            leading=self.fontProps.leading,
128            letterSpacing=self.fontProps.letterSpacing,
129            precision=1,
130        )
131        self.fontProps.updateFontSize(fs).apply()
132        return self
133
134    def draw(self):
135        """
136        Draws the composed text into the frame.
137        """
138        drawBot.textBox(self.fString, self.frame.coords)
class KTextComposer:
 15class KTextComposer:
 16    """Composes and manages formatted text within a given frame."""
 17
 18    def __init__(
 19        self,
 20        frame: KBox,
 21        fontProps: "DFontProps",
 22        pool: KPool,
 23        formattedArgs: dict = None,
 24    ):
 25        """
 26        Initializes the text composer with a frame, font properties, and a word pool.
 27
 28        Args:
 29            frame: The frame within which the text will be composed.
 30            fontProps: The font properties to be applied to the text.
 31            pool: The word pool to be used for text composition.
 32            formattedArgs: Additional arguments for formatting the text.
 33        """
 34        self.frame = frame
 35        self.fontProps = fontProps.apply()
 36        self.formattedArgs = formattedArgs if formattedArgs else dict()
 37        self.fString = drawBot.FormattedString(
 38            **self.fontProps.calc(), **self.formattedArgs
 39        )
 40        self.pool = pool
 41
 42    @property
 43    def height(self) -> float:
 44        """
 45        Calculates the height of the composed text.
 46
 47        Returns:
 48            The height of the text in points.
 49        """
 50        return drawBot.textSize(self.fString, width=self.frame.width)[1]
 51
 52    def setPool(self, pool: KPool):
 53        """
 54        Sets the word pool for the composer.
 55        """
 56        self.pool = pool
 57        return self
 58
 59    def setOpenTypeFeatures(self, **features: dict[fonts.FeatureId, bool]):
 60        """
 61        Sets OpenType features for the formatted string.
 62        """
 63        drawBot.openTypeFeatures(**features)
 64        self.fString.openTypeFeatures(**features)
 65        return self
 66
 67    def addWord(
 68        self,
 69        width: float,
 70        shape: str = None,
 71        tokens: list[content.CharacterToken] = None,
 72    ):
 73        """
 74        Adds a word from the pool that fits the specified width and optional filters.
 75        """
 76        if not self.pool:
 77            raise AttributeError("No pool set. Use setPool() to set a pool.")
 78
 79        linePool = copy.copy(self.pool)  # ? Create a pool for this line
 80
 81        if shape:
 82            linePool = linePool.filterWithRegex(shape)
 83
 84        if tokens:
 85            linePool = linePool.filterTokens(tokens)
 86
 87        word = linePool.getItemByWidth(self.frame.width * width)
 88        # Skip removing from the main pool: Commit June 14, 2025
 89        self._append(word)
 90        return self
 91
 92    def addString(self, string: str):
 93        """
 94        Adds a raw string to the formatted text.
 95        """
 96        self._append(string)
 97        return self
 98
 99    def _append(self, string: str):
100        """
101        Helper method to append text to the formatted string, ensuring that formatting is preserved.
102        """
103        # ? Reapply formatted args: spacing needs to be explicit in case it changed
104        self.fString.append(string, **self.fontProps.calc(), **self.formattedArgs)
105
106    def breakLine(self):
107        """
108        Inserts a line break in the formatted text.
109        """
110        self.fString.append("\n")
111        return self
112
113    def setTab(self, width: float, alignment: fonts.TextAlign):
114        """
115        Sets a tab stop at the given width and alignment, then inserts a tab.
116        """
117        self.fString.tabs((self.frame.width * width, alignment))
118        self.fString.append("\t")
119        return self
120
121    def fitForString(self, string: str):
122        """
123        Adjusts font size to fit the string on a single line within the frame. Updates `fontProps` as a side effect.
124        """
125        fs, *_ = fonts.fitBinary(
126            string,
127            self.frame.coords,
128            leading=self.fontProps.leading,
129            letterSpacing=self.fontProps.letterSpacing,
130            precision=1,
131        )
132        self.fontProps.updateFontSize(fs).apply()
133        return self
134
135    def draw(self):
136        """
137        Draws the composed text into the frame.
138        """
139        drawBot.textBox(self.fString, self.frame.coords)

Composes and manages formatted text within a given frame.

KTextComposer( frame: classes.c20_box.KBox, fontProps: datatypes.data_fontprops.DFontProps, pool: classes.c32_pool.KPool, formattedArgs: dict = None)
18    def __init__(
19        self,
20        frame: KBox,
21        fontProps: "DFontProps",
22        pool: KPool,
23        formattedArgs: dict = None,
24    ):
25        """
26        Initializes the text composer with a frame, font properties, and a word pool.
27
28        Args:
29            frame: The frame within which the text will be composed.
30            fontProps: The font properties to be applied to the text.
31            pool: The word pool to be used for text composition.
32            formattedArgs: Additional arguments for formatting the text.
33        """
34        self.frame = frame
35        self.fontProps = fontProps.apply()
36        self.formattedArgs = formattedArgs if formattedArgs else dict()
37        self.fString = drawBot.FormattedString(
38            **self.fontProps.calc(), **self.formattedArgs
39        )
40        self.pool = pool

Initializes the text composer with a frame, font properties, and a word pool.

Arguments:
  • frame: The frame within which the text will be composed.
  • fontProps: The font properties to be applied to the text.
  • pool: The word pool to be used for text composition.
  • formattedArgs: Additional arguments for formatting the text.
frame
fontProps
formattedArgs
fString
pool
height: float
42    @property
43    def height(self) -> float:
44        """
45        Calculates the height of the composed text.
46
47        Returns:
48            The height of the text in points.
49        """
50        return drawBot.textSize(self.fString, width=self.frame.width)[1]

Calculates the height of the composed text.

Returns:

The height of the text in points.

def setPool(self, pool: classes.c32_pool.KPool):
52    def setPool(self, pool: KPool):
53        """
54        Sets the word pool for the composer.
55        """
56        self.pool = pool
57        return self

Sets the word pool for the composer.

def setOpenTypeFeatures( self, **features: dict[typing.Literal['liga', 'dlig', 'calt', 'locl', 'titl', 'case', 'pnum', 'lnum', 'onum', 'tnum', 'zero', 'subs', 'sups', 'numr', 'dnom', 'frac', 'ordn', 'kern', 'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'ss07', 'ss08', 'ss09', 'ss10'], bool]):
59    def setOpenTypeFeatures(self, **features: dict[fonts.FeatureId, bool]):
60        """
61        Sets OpenType features for the formatted string.
62        """
63        drawBot.openTypeFeatures(**features)
64        self.fString.openTypeFeatures(**features)
65        return self

Sets OpenType features for the formatted string.

def addWord( self, width: float, shape: str = None, tokens: list[typing.Literal['word', 'nonword']] = None):
67    def addWord(
68        self,
69        width: float,
70        shape: str = None,
71        tokens: list[content.CharacterToken] = None,
72    ):
73        """
74        Adds a word from the pool that fits the specified width and optional filters.
75        """
76        if not self.pool:
77            raise AttributeError("No pool set. Use setPool() to set a pool.")
78
79        linePool = copy.copy(self.pool)  # ? Create a pool for this line
80
81        if shape:
82            linePool = linePool.filterWithRegex(shape)
83
84        if tokens:
85            linePool = linePool.filterTokens(tokens)
86
87        word = linePool.getItemByWidth(self.frame.width * width)
88        # Skip removing from the main pool: Commit June 14, 2025
89        self._append(word)
90        return self

Adds a word from the pool that fits the specified width and optional filters.

def addString(self, string: str):
92    def addString(self, string: str):
93        """
94        Adds a raw string to the formatted text.
95        """
96        self._append(string)
97        return self

Adds a raw string to the formatted text.

def breakLine(self):
106    def breakLine(self):
107        """
108        Inserts a line break in the formatted text.
109        """
110        self.fString.append("\n")
111        return self

Inserts a line break in the formatted text.

def setTab(self, width: float, alignment: Literal['left', 'center', 'right']):
113    def setTab(self, width: float, alignment: fonts.TextAlign):
114        """
115        Sets a tab stop at the given width and alignment, then inserts a tab.
116        """
117        self.fString.tabs((self.frame.width * width, alignment))
118        self.fString.append("\t")
119        return self

Sets a tab stop at the given width and alignment, then inserts a tab.

def fitForString(self, string: str):
121    def fitForString(self, string: str):
122        """
123        Adjusts font size to fit the string on a single line within the frame. Updates `fontProps` as a side effect.
124        """
125        fs, *_ = fonts.fitBinary(
126            string,
127            self.frame.coords,
128            leading=self.fontProps.leading,
129            letterSpacing=self.fontProps.letterSpacing,
130            precision=1,
131        )
132        self.fontProps.updateFontSize(fs).apply()
133        return self

Adjusts font size to fit the string on a single line within the frame. Updates fontProps as a side effect.

def draw(self):
135    def draw(self):
136        """
137        Draws the composed text into the frame.
138        """
139        drawBot.textBox(self.fString, self.frame.coords)

Draws the composed text into the frame.