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.
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.
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.