Value object that knows how to fit text into given rectangular extents.
| 11 | |
| 12 | |
| 13 | class TextFitter(tuple): |
| 14 | """Value object that knows how to fit text into given rectangular extents.""" |
| 15 | |
| 16 | def __new__(cls, line_source, extents, font_file): |
| 17 | width, height = extents |
| 18 | return tuple.__new__(cls, (line_source, width, height, font_file)) |
| 19 | |
| 20 | @classmethod |
| 21 | def best_fit_font_size( |
| 22 | cls, text: str, extents: tuple[Length, Length], max_size: int, font_file: str |
| 23 | ) -> int: |
| 24 | """Return whole-number best fit point size less than or equal to `max_size`. |
| 25 | |
| 26 | The return value is the largest whole-number point size less than or equal to |
| 27 | `max_size` that allows `text` to fit completely within `extents` when rendered |
| 28 | using font defined in `font_file`. |
| 29 | """ |
| 30 | line_source = _LineSource(text) |
| 31 | text_fitter = cls(line_source, extents, font_file) |
| 32 | return text_fitter._best_fit_font_size(max_size) |
| 33 | |
| 34 | def _best_fit_font_size(self, max_size): |
| 35 | """ |
| 36 | Return the largest whole-number point size less than or equal to |
| 37 | *max_size* that this fitter can fit. |
| 38 | """ |
| 39 | predicate = self._fits_inside_predicate |
| 40 | sizes = _BinarySearchTree.from_ordered_sequence(range(1, int(max_size) + 1)) |
| 41 | return sizes.find_max(predicate) |
| 42 | |
| 43 | def _break_line(self, line_source, point_size): |
| 44 | """ |
| 45 | Return a (line, remainder) pair where *line* is the longest line in |
| 46 | *line_source* that will fit in this fitter's width and *remainder* is |
| 47 | a |_LineSource| object containing the text following the break point. |
| 48 | """ |
| 49 | lines = _BinarySearchTree.from_ordered_sequence(line_source) |
| 50 | predicate = self._fits_in_width_predicate(point_size) |
| 51 | return lines.find_max(predicate) |
| 52 | |
| 53 | def _fits_in_width_predicate(self, point_size): |
| 54 | """ |
| 55 | Return a function taking a text string value and returns |True| if |
| 56 | that text fits in this fitter when rendered at *point_size*. Used as |
| 57 | predicate for _break_line() |
| 58 | """ |
| 59 | |
| 60 | def predicate(line): |
| 61 | """ |
| 62 | Return |True| if *line* fits in this fitter when rendered at |
| 63 | *point_size*. |
| 64 | """ |
| 65 | cx = _rendered_size(line.text, point_size, self._font_file)[0] |
| 66 | return cx <= self._width |
| 67 | |
| 68 | return predicate |
| 69 | |
| 70 | @property |
no outgoing calls
searching dependent graphs…