Skip to main content

tui_big_text/
big_text.rs

1use alloc::vec::Vec;
2use core::cmp::min;
3
4use derive_builder::Builder;
5use font8x8::UnicodeFonts;
6use ratatui_core::buffer::Buffer;
7use ratatui_core::layout::{Alignment, Rect};
8use ratatui_core::style::Style;
9use ratatui_core::text::{Line, StyledGrapheme};
10use ratatui_core::widgets::Widget;
11use ratatui_widgets::block::{Block, BlockExt};
12
13use crate::PixelSize;
14
15/// Displays one or more lines of text using 8x8 pixel characters.
16///
17/// The text is rendered using the [font8x8](https://crates.io/crates/font8x8) crate.
18///
19/// Using the `pixel_size` method, you can also chose, how 'big' a pixel should be. Currently a
20/// pixel of the 8x8 font can be represented by one full or half (horizontal/vertical/both)
21/// character cell of the terminal.
22///
23/// # Examples
24///
25/// ```rust
26/// use ratatui::prelude::*;
27/// use tui_big_text::{BigText, PixelSize};
28///
29/// BigText::builder()
30///     .pixel_size(PixelSize::Full)
31///     .style(Style::new().white())
32///     .lines(vec![
33///         "Hello".red().into(),
34///         "World".blue().into(),
35///         "=====".into(),
36///     ])
37///     .build();
38/// ```
39///
40/// Renders:
41///
42/// ```plain
43/// ██  ██           ███     ███
44/// ██  ██            ██      ██
45/// ██  ██   ████     ██      ██     ████
46/// ██████  ██  ██    ██      ██    ██  ██
47/// ██  ██  ██████    ██      ██    ██  ██
48/// ██  ██  ██        ██      ██    ██  ██
49/// ██  ██   ████    ████    ████    ████
50///
51/// ██   ██                  ███       ███
52/// ██   ██                   ██        ██
53/// ██   ██  ████   ██ ███    ██        ██
54/// ██ █ ██ ██  ██   ███ ██   ██     █████
55/// ███████ ██  ██   ██  ██   ██    ██  ██
56/// ███ ███ ██  ██   ██       ██    ██  ██
57/// ██   ██  ████   ████     ████    ███ ██
58///
59///  ███ ██  ███ ██  ███ ██  ███ ██  ███ ██
60/// ██ ███  ██ ███  ██ ███  ██ ███  ██ ███
61/// ```
62#[derive(Debug, Builder, Clone, PartialEq, Eq, Hash)]
63#[builder(build_fn(skip), no_std)]
64#[non_exhaustive]
65pub struct BigText<'a> {
66    /// The text to display
67    #[builder(default, setter(into))]
68    pub lines: Vec<Line<'a>>,
69
70    /// The style of the widget
71    ///
72    /// Defaults to `Style::default()`
73    #[builder(default, setter(into))]
74    pub style: Style,
75
76    /// The size of single glyphs
77    ///
78    /// Defaults to `PixelSize::default()` (=> PixelSize::Full)
79    #[builder(default)]
80    pub pixel_size: PixelSize,
81
82    /// The horizontal alignment of the text
83    ///
84    /// Defaults to `Alignment::default()` (=> Alignment::Left)
85    #[builder(default)]
86    pub alignment: Alignment,
87
88    /// Block to wrap the widget in
89    #[builder(default, setter(into, strip_option))]
90    pub block: Option<Block<'a>>,
91}
92
93impl BigText<'static> {
94    /// Create a new [`BigTextBuilder`] to configure a [`BigText`] widget.
95    pub fn builder() -> BigTextBuilder<'static> {
96        BigTextBuilder::default()
97    }
98}
99
100impl BigTextBuilder<'_> {
101    /// Set the alignment of the text.
102    pub fn left_aligned(&mut self) -> &mut Self {
103        self.alignment(Alignment::Left)
104    }
105
106    /// Set the alignment of the text.
107    pub fn right_aligned(&mut self) -> &mut Self {
108        self.alignment(Alignment::Right)
109    }
110
111    /// Set the alignment of the text.
112    pub fn centered(&mut self) -> &mut Self {
113        self.alignment(Alignment::Center)
114    }
115}
116
117impl<'a> BigTextBuilder<'a> {
118    /// Build the [`BigText`] widget.
119    pub fn build(&self) -> BigText<'a> {
120        BigText {
121            lines: self.lines.as_ref().cloned().unwrap_or_default(),
122            style: self.style.unwrap_or_default(),
123            pixel_size: self.pixel_size.unwrap_or_default(),
124            alignment: self.alignment.unwrap_or_default(),
125            block: self.block.as_ref().cloned().unwrap_or_default(),
126        }
127    }
128}
129
130impl Widget for BigText<'_> {
131    fn render(self, area: Rect, buf: &mut Buffer) {
132        (&self).render(area, buf);
133    }
134}
135
136impl Widget for &BigText<'_> {
137    fn render(self, area: Rect, buf: &mut Buffer) {
138        self.block.as_ref().render(area, buf);
139        let inner = self.block.inner_if_some(area);
140
141        let layout = layout(inner, &self.pixel_size, self.alignment, &self.lines);
142        for (line, line_layout) in self.lines.iter().zip(layout) {
143            for (g, cell) in line.styled_graphemes(self.style).zip(line_layout) {
144                render_symbol(g, cell, buf, &self.pixel_size);
145            }
146        }
147    }
148}
149
150/// Chunk the area into as many x*y cells as possible returned as a 2D iterator of `Rect`s
151/// representing the rows of cells. The size of each cell depends on given font size
152fn layout<'a>(
153    area: Rect,
154    pixel_size: &PixelSize,
155    alignment: Alignment,
156    lines: &'a [Line<'a>],
157) -> impl IntoIterator<Item = impl IntoIterator<Item = Rect> + use<>> + 'a + use<'a> {
158    let (step_x, step_y) = pixel_size.pixels_per_cell();
159    let width = 8_u16.div_ceil(step_x);
160    let height = 8_u16.div_ceil(step_y);
161
162    (area.top()..area.bottom())
163        .step_by(height as usize)
164        .zip(lines.iter())
165        .map(move |(y, line)| {
166            let offset = get_alignment_offset(area.width, width, alignment, line);
167            (area.left() + offset..area.right())
168                .step_by(width as usize)
169                .map(move |x| {
170                    let width = min(area.right() - x, width);
171                    let height = min(area.bottom() - y, height);
172                    Rect::new(x, y, width, height)
173                })
174        })
175}
176
177fn get_alignment_offset<'a>(
178    area_width: u16,
179    letter_width: u16,
180    alignment: Alignment,
181    line: &'a Line<'a>,
182) -> u16 {
183    // Each grapheme occupies one fixed-width bitmap area in the rendered layout.
184    let line_glyph_count = line.styled_graphemes(Style::default()).count();
185    let big_line_width = line_glyph_count
186        .saturating_mul(usize::from(letter_width))
187        .clamp(0, usize::from(u16::MAX)) as u16;
188    match alignment {
189        Alignment::Center => (area_width / 2).saturating_sub(big_line_width / 2),
190        Alignment::Right => area_width.saturating_sub(big_line_width),
191        Alignment::Left => 0,
192    }
193}
194
195const FONTS: [&dyn UnicodeFonts; 8] = [
196    &font8x8::BASIC_FONTS,
197    &font8x8::LATIN_FONTS,
198    &font8x8::HIRAGANA_FONTS,
199    &font8x8::GREEK_FONTS,
200    &font8x8::BLOCK_FONTS,
201    &font8x8::BOX_FONTS,
202    &font8x8::MISC_FONTS,
203    &font8x8::SGA_FONTS,
204];
205
206/// Render a single grapheme into a cell by looking up the corresponding 8x8 bitmap in all of the
207/// available fonts and setting the corresponding cells in the buffer.
208fn render_symbol(grapheme: StyledGrapheme, area: Rect, buf: &mut Buffer, pixel_size: &PixelSize) {
209    buf.set_style(area, grapheme.style);
210    let c = grapheme.symbol.chars().next().unwrap(); // TODO: handle multi-char graphemes
211    if let Some(glyph) = FONTS.iter().find_map(|font| font.get(c)) {
212        render_glyph(glyph, area, buf, pixel_size);
213    }
214}
215
216/// Render a single 8x8 glyph into a cell by setting the corresponding cells in the buffer.
217fn render_glyph(glyph: [u8; 8], area: Rect, buf: &mut Buffer, pixel_size: &PixelSize) {
218    let (step_x, step_y) = pixel_size.pixels_per_cell();
219
220    let glyph_vertical_index = (0..glyph.len()).step_by(step_y as usize);
221    let glyph_horizontal_bit_selector = (0..8).step_by(step_x as usize);
222
223    for (y, row) in glyph_vertical_index.zip(area.rows()) {
224        for (x, col) in glyph_horizontal_bit_selector.clone().zip(row.columns()) {
225            buf[col].set_char(pixel_size.symbol_for_position(&glyph, y, x));
226        }
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use alloc::vec;
233
234    use ratatui_core::style::Stylize;
235
236    use super::*;
237
238    #[test]
239    fn build() {
240        let lines = vec![Line::from(vec!["Hello".red(), "World".blue()])];
241        let style = Style::new().green();
242        let pixel_size = PixelSize::default();
243        let alignment = Alignment::Center;
244        assert_eq!(
245            BigText::builder()
246                .lines(lines.clone())
247                .style(style)
248                .alignment(Alignment::Center)
249                .build(),
250            BigText {
251                lines,
252                style,
253                pixel_size,
254                alignment,
255                block: None,
256            }
257        );
258    }
259
260    #[test]
261    fn render_single_line() {
262        let big_text = BigText::builder()
263            .lines(vec![Line::from("SingleLine")])
264            .build();
265        let mut buf = Buffer::empty(Rect::new(0, 0, 80, 8));
266        big_text.render(buf.area, &mut buf);
267        let expected = Buffer::with_lines(vec![
268            " ████     ██                     ███            ████      ██                    ",
269            "██  ██                            ██             ██                             ",
270            "███      ███    █████    ███ ██   ██     ████    ██      ███    █████    ████   ",
271            " ███      ██    ██  ██  ██  ██    ██    ██  ██   ██       ██    ██  ██  ██  ██  ",
272            "   ███    ██    ██  ██  ██  ██    ██    ██████   ██   █   ██    ██  ██  ██████  ",
273            "██  ██    ██    ██  ██   █████    ██    ██       ██  ██   ██    ██  ██  ██      ",
274            " ████    ████   ██  ██      ██   ████    ████   ███████  ████   ██  ██   ████   ",
275            "                        █████                                                   ",
276        ]);
277        assert_eq!(buf, expected);
278    }
279
280    #[test]
281    fn render_by_reference_matches_owned_render() {
282        let big_text = BigText::builder()
283            .lines(vec![Line::from("Reference")])
284            .build();
285        let mut owned_buf = Buffer::empty(Rect::new(0, 0, 72, 8));
286        let mut borrowed_buf = Buffer::empty(Rect::new(0, 0, 72, 8));
287
288        big_text.clone().render(owned_buf.area, &mut owned_buf);
289        (&big_text).render(borrowed_buf.area, &mut borrowed_buf);
290
291        assert_eq!(borrowed_buf, owned_buf);
292    }
293
294    #[test]
295    fn render_truncated() {
296        let big_text = BigText::builder()
297            .lines(vec![Line::from("Truncated")])
298            .build();
299        let mut buf = Buffer::empty(Rect::new(0, 0, 70, 6));
300        big_text.render(buf.area, &mut buf);
301        let expected = Buffer::with_lines(vec![
302            "██████                                             █               ███",
303            "█ ██ █                                            ██                ██",
304            "  ██    ██ ███  ██  ██  █████    ████    ████    █████   ████       ██",
305            "  ██     ███ ██ ██  ██  ██  ██  ██  ██      ██    ██    ██  ██   █████",
306            "  ██     ██  ██ ██  ██  ██  ██  ██       █████    ██    ██████  ██  ██",
307            "  ██     ██     ██  ██  ██  ██  ██  ██  ██  ██    ██ █  ██      ██  ██",
308        ]);
309        assert_eq!(buf, expected);
310    }
311
312    #[test]
313    fn render_multiple_lines() {
314        let big_text = BigText::builder()
315            .lines(vec![Line::from("Multi"), Line::from("Lines")])
316            .build();
317        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 16));
318        big_text.render(buf.area, &mut buf);
319        let expected = Buffer::with_lines(vec![
320            "██   ██          ███       █      ██    ",
321            "███ ███           ██      ██            ",
322            "███████ ██  ██    ██     █████   ███    ",
323            "███████ ██  ██    ██      ██      ██    ",
324            "██ █ ██ ██  ██    ██      ██      ██    ",
325            "██   ██ ██  ██    ██      ██ █    ██    ",
326            "██   ██  ███ ██  ████      ██    ████   ",
327            "                                        ",
328            "████      ██                            ",
329            " ██                                     ",
330            " ██      ███    █████    ████    █████  ",
331            " ██       ██    ██  ██  ██  ██  ██      ",
332            " ██   █   ██    ██  ██  ██████   ████   ",
333            " ██  ██   ██    ██  ██  ██          ██  ",
334            "███████  ████   ██  ██   ████   █████   ",
335            "                                        ",
336        ]);
337        assert_eq!(buf, expected);
338    }
339
340    #[test]
341    fn render_widget_style() {
342        let big_text = BigText::builder()
343            .lines(vec![Line::from("Styled")])
344            .style(Style::new().bold())
345            .build();
346        let mut buf = Buffer::empty(Rect::new(0, 0, 48, 8));
347        big_text.render(buf.area, &mut buf);
348        let mut expected = Buffer::with_lines(vec![
349            " ████      █             ███               ███  ",
350            "██  ██    ██              ██                ██  ",
351            "███      █████  ██  ██    ██     ████       ██  ",
352            " ███      ██    ██  ██    ██    ██  ██   █████  ",
353            "   ███    ██    ██  ██    ██    ██████  ██  ██  ",
354            "██  ██    ██ █   █████    ██    ██      ██  ██  ",
355            " ████      ██       ██   ████    ████    ███ ██ ",
356            "                █████                           ",
357        ]);
358        expected.set_style(Rect::new(0, 0, 48, 8), Style::new().bold());
359        assert_eq!(buf, expected);
360    }
361
362    #[test]
363    fn render_line_style() {
364        let big_text = BigText::builder()
365            .lines(vec![
366                Line::from("Red".red()),
367                Line::from("Green".green()),
368                Line::from("Blue".blue()),
369            ])
370            .build();
371        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 24));
372        big_text.render(buf.area, &mut buf);
373        let mut expected = Buffer::with_lines(vec![
374            "██████             ███                  ",
375            " ██  ██             ██                  ",
376            " ██  ██  ████       ██                  ",
377            " █████  ██  ██   █████                  ",
378            " ██ ██  ██████  ██  ██                  ",
379            " ██  ██ ██      ██  ██                  ",
380            "███  ██  ████    ███ ██                 ",
381            "                                        ",
382            "  ████                                  ",
383            " ██  ██                                 ",
384            "██      ██ ███   ████    ████   █████   ",
385            "██       ███ ██ ██  ██  ██  ██  ██  ██  ",
386            "██  ███  ██  ██ ██████  ██████  ██  ██  ",
387            " ██  ██  ██     ██      ██      ██  ██  ",
388            "  █████ ████     ████    ████   ██  ██  ",
389            "                                        ",
390            "██████   ███                            ",
391            " ██  ██   ██                            ",
392            " ██  ██   ██    ██  ██   ████           ",
393            " █████    ██    ██  ██  ██  ██          ",
394            " ██  ██   ██    ██  ██  ██████          ",
395            " ██  ██   ██    ██  ██  ██              ",
396            "██████   ████    ███ ██  ████           ",
397            "                                        ",
398        ]);
399        expected.set_style(Rect::new(0, 0, 24, 8), Style::new().red());
400        expected.set_style(Rect::new(0, 8, 40, 8), Style::new().green());
401        expected.set_style(Rect::new(0, 16, 32, 8), Style::new().blue());
402        assert_eq!(buf, expected);
403    }
404
405    #[test]
406    fn render_half_height_single_line() {
407        let big_text = BigText::builder()
408            .pixel_size(PixelSize::HalfHeight)
409            .lines(vec![Line::from("SingleLine")])
410            .build();
411        let mut buf = Buffer::empty(Rect::new(0, 0, 80, 4));
412        big_text.render(buf.area, &mut buf);
413        let expected = Buffer::with_lines(vec![
414            "▄█▀▀█▄    ▀▀                     ▀██            ▀██▀      ▀▀                    ",
415            "▀██▄     ▀██    ██▀▀█▄  ▄█▀▀▄█▀   ██    ▄█▀▀█▄   ██      ▀██    ██▀▀█▄  ▄█▀▀█▄  ",
416            "▄▄ ▀██    ██    ██  ██  ▀█▄▄██    ██    ██▀▀▀▀   ██  ▄█   ██    ██  ██  ██▀▀▀▀  ",
417            " ▀▀▀▀    ▀▀▀▀   ▀▀  ▀▀  ▄▄▄▄█▀   ▀▀▀▀    ▀▀▀▀   ▀▀▀▀▀▀▀  ▀▀▀▀   ▀▀  ▀▀   ▀▀▀▀   ",
418        ]);
419        assert_eq!(buf, expected);
420    }
421
422    #[test]
423    fn render_half_height_truncated() {
424        let big_text = BigText::builder()
425            .pixel_size(PixelSize::HalfHeight)
426            .lines(vec![Line::from("Truncated")])
427            .build();
428        let mut buf = Buffer::empty(Rect::new(0, 0, 70, 3));
429        big_text.render(buf.area, &mut buf);
430        let expected = Buffer::with_lines(vec![
431            "█▀██▀█                                            ▄█               ▀██",
432            "  ██    ▀█▄█▀█▄ ██  ██  ██▀▀█▄  ▄█▀▀█▄   ▀▀▀█▄   ▀██▀▀  ▄█▀▀█▄   ▄▄▄██",
433            "  ██     ██  ▀▀ ██  ██  ██  ██  ██  ▄▄  ▄█▀▀██    ██ ▄  ██▀▀▀▀  ██  ██",
434        ]);
435        assert_eq!(buf, expected);
436    }
437
438    #[test]
439    fn render_half_height_multiple_lines() {
440        let big_text = BigText::builder()
441            .pixel_size(PixelSize::HalfHeight)
442            .lines(vec![Line::from("Multi"), Line::from("Lines")])
443            .build();
444        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 8));
445        big_text.render(buf.area, &mut buf);
446        let expected = Buffer::with_lines(vec![
447            "██▄ ▄██          ▀██      ▄█      ▀▀    ",
448            "███████ ██  ██    ██     ▀██▀▀   ▀██    ",
449            "██ ▀ ██ ██  ██    ██      ██ ▄    ██    ",
450            "▀▀   ▀▀  ▀▀▀ ▀▀  ▀▀▀▀      ▀▀    ▀▀▀▀   ",
451            "▀██▀      ▀▀                            ",
452            " ██      ▀██    ██▀▀█▄  ▄█▀▀█▄  ▄█▀▀▀▀  ",
453            " ██  ▄█   ██    ██  ██  ██▀▀▀▀   ▀▀▀█▄  ",
454            "▀▀▀▀▀▀▀  ▀▀▀▀   ▀▀  ▀▀   ▀▀▀▀   ▀▀▀▀▀   ",
455        ]);
456        assert_eq!(buf, expected);
457    }
458
459    #[test]
460    fn render_half_height_widget_style() {
461        let big_text = BigText::builder()
462            .pixel_size(PixelSize::HalfHeight)
463            .lines(vec![Line::from("Styled")])
464            .style(Style::new().bold())
465            .build();
466        let mut buf = Buffer::empty(Rect::new(0, 0, 48, 4));
467        big_text.render(buf.area, &mut buf);
468        let mut expected = Buffer::with_lines(vec![
469            "▄█▀▀█▄    ▄█             ▀██               ▀██  ",
470            "▀██▄     ▀██▀▀  ██  ██    ██    ▄█▀▀█▄   ▄▄▄██  ",
471            "▄▄ ▀██    ██ ▄  ▀█▄▄██    ██    ██▀▀▀▀  ██  ██  ",
472            " ▀▀▀▀      ▀▀   ▄▄▄▄█▀   ▀▀▀▀    ▀▀▀▀    ▀▀▀ ▀▀ ",
473        ]);
474        expected.set_style(Rect::new(0, 0, 48, 4), Style::new().bold());
475        assert_eq!(buf, expected);
476    }
477
478    #[test]
479    fn render_half_height_line_style() {
480        let big_text = BigText::builder()
481            .pixel_size(PixelSize::HalfHeight)
482            .lines(vec![
483                Line::from("Red".red()),
484                Line::from("Green".green()),
485                Line::from("Blue".blue()),
486            ])
487            .build();
488        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 12));
489        big_text.render(buf.area, &mut buf);
490        let mut expected = Buffer::with_lines(vec![
491            "▀██▀▀█▄            ▀██                  ",
492            " ██▄▄█▀ ▄█▀▀█▄   ▄▄▄██                  ",
493            " ██ ▀█▄ ██▀▀▀▀  ██  ██                  ",
494            "▀▀▀  ▀▀  ▀▀▀▀    ▀▀▀ ▀▀                 ",
495            " ▄█▀▀█▄                                 ",
496            "██      ▀█▄█▀█▄ ▄█▀▀█▄  ▄█▀▀█▄  ██▀▀█▄  ",
497            "▀█▄ ▀██  ██  ▀▀ ██▀▀▀▀  ██▀▀▀▀  ██  ██  ",
498            "  ▀▀▀▀▀ ▀▀▀▀     ▀▀▀▀    ▀▀▀▀   ▀▀  ▀▀  ",
499            "▀██▀▀█▄  ▀██                            ",
500            " ██▄▄█▀   ██    ██  ██  ▄█▀▀█▄          ",
501            " ██  ██   ██    ██  ██  ██▀▀▀▀          ",
502            "▀▀▀▀▀▀   ▀▀▀▀    ▀▀▀ ▀▀  ▀▀▀▀           ",
503        ]);
504        expected.set_style(Rect::new(0, 0, 24, 4), Style::new().red());
505        expected.set_style(Rect::new(0, 4, 40, 4), Style::new().green());
506        expected.set_style(Rect::new(0, 8, 32, 4), Style::new().blue());
507        assert_eq!(buf, expected);
508    }
509
510    #[test]
511    fn render_half_width_single_line() {
512        let big_text = BigText::builder()
513            .pixel_size(PixelSize::HalfWidth)
514            .lines(vec![Line::from("SingleLine")])
515            .build();
516        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 8));
517        big_text.render(buf.area, &mut buf);
518        let expected = Buffer::with_lines(vec![
519            "▐█▌  █          ▐█      ██   █          ",
520            "█ █              █      ▐▌              ",
521            "█▌  ▐█  ██▌ ▐█▐▌ █  ▐█▌ ▐▌  ▐█  ██▌ ▐█▌ ",
522            "▐█   █  █ █ █ █  █  █ █ ▐▌   █  █ █ █ █ ",
523            " ▐█  █  █ █ █ █  █  ███ ▐▌ ▌ █  █ █ ███ ",
524            "█ █  █  █ █ ▐██  █  █   ▐▌▐▌ █  █ █ █   ",
525            "▐█▌ ▐█▌ █ █   █ ▐█▌ ▐█▌ ███▌▐█▌ █ █ ▐█▌ ",
526            "            ██▌                         ",
527        ]);
528        assert_eq!(buf, expected);
529    }
530
531    #[test]
532    fn render_half_width_truncated() {
533        let big_text = BigText::builder()
534            .pixel_size(PixelSize::HalfWidth)
535            .lines(vec![Line::from("Truncated")])
536            .build();
537        let mut buf = Buffer::empty(Rect::new(0, 0, 35, 6));
538        big_text.render(buf.area, &mut buf);
539        let expected = Buffer::with_lines(vec![
540            "███                      ▐       ▐█",
541            "▌█▐                      █        █",
542            " █  █▐█ █ █ ██▌ ▐█▌ ▐█▌ ▐██ ▐█▌   █",
543            " █  ▐█▐▌█ █ █ █ █ █   █  █  █ █ ▐██",
544            " █  ▐▌▐▌█ █ █ █ █   ▐██  █  ███ █ █",
545            " █  ▐▌  █ █ █ █ █ █ █ █  █▐ █   █ █",
546        ]);
547        assert_eq!(buf, expected);
548    }
549
550    #[test]
551    fn render_half_width_multiple_lines() {
552        let big_text = BigText::builder()
553            .pixel_size(PixelSize::HalfWidth)
554            .lines(vec![Line::from("Multi"), Line::from("Lines")])
555            .build();
556        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 16));
557        big_text.render(buf.area, &mut buf);
558        let expected = Buffer::with_lines(vec![
559            "█ ▐▌    ▐█   ▐   █  ",
560            "█▌█▌     █   █      ",
561            "███▌█ █  █  ▐██ ▐█  ",
562            "███▌█ █  █   █   █  ",
563            "█▐▐▌█ █  █   █   █  ",
564            "█ ▐▌█ █  █   █▐  █  ",
565            "█ ▐▌▐█▐▌▐█▌  ▐▌ ▐█▌ ",
566            "                    ",
567            "██   █              ",
568            "▐▌                  ",
569            "▐▌  ▐█  ██▌ ▐█▌ ▐██ ",
570            "▐▌   █  █ █ █ █ █   ",
571            "▐▌ ▌ █  █ █ ███ ▐█▌ ",
572            "▐▌▐▌ █  █ █ █     █ ",
573            "███▌▐█▌ █ █ ▐█▌ ██▌ ",
574            "                    ",
575        ]);
576        assert_eq!(buf, expected);
577    }
578
579    #[test]
580    fn render_half_width_widget_style() {
581        let big_text = BigText::builder()
582            .pixel_size(PixelSize::HalfWidth)
583            .lines(vec![Line::from("Styled")])
584            .style(Style::new().bold())
585            .build();
586        let mut buf = Buffer::empty(Rect::new(0, 0, 24, 8));
587        big_text.render(buf.area, &mut buf);
588        let mut expected = Buffer::with_lines(vec![
589            "▐█▌  ▐      ▐█       ▐█ ",
590            "█ █  █       █        █ ",
591            "█▌  ▐██ █ █  █  ▐█▌   █ ",
592            "▐█   █  █ █  █  █ █ ▐██ ",
593            " ▐█  █  █ █  █  ███ █ █ ",
594            "█ █  █▐ ▐██  █  █   █ █ ",
595            "▐█▌  ▐▌   █ ▐█▌ ▐█▌ ▐█▐▌",
596            "        ██▌             ",
597        ]);
598        expected.set_style(Rect::new(0, 0, 24, 8), Style::new().bold());
599        assert_eq!(buf, expected);
600    }
601
602    #[test]
603    fn render_half_width_line_style() {
604        let big_text = BigText::builder()
605            .pixel_size(PixelSize::HalfWidth)
606            .lines(vec![
607                Line::from("Red".red()),
608                Line::from("Green".green()),
609                Line::from("Blue".blue()),
610            ])
611            .build();
612        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 24));
613        big_text.render(buf.area, &mut buf);
614        let mut expected = Buffer::with_lines(vec![
615            "███      ▐█         ",
616            "▐▌▐▌      █         ",
617            "▐▌▐▌▐█▌   █         ",
618            "▐██ █ █ ▐██         ",
619            "▐▌█ ███ █ █         ",
620            "▐▌▐▌█   █ █         ",
621            "█▌▐▌▐█▌ ▐█▐▌        ",
622            "                    ",
623            " ██                 ",
624            "▐▌▐▌                ",
625            "█   █▐█ ▐█▌ ▐█▌ ██▌ ",
626            "█   ▐█▐▌█ █ █ █ █ █ ",
627            "█ █▌▐▌▐▌███ ███ █ █ ",
628            "▐▌▐▌▐▌  █   █   █ █ ",
629            " ██▌██  ▐█▌ ▐█▌ █ █ ",
630            "                    ",
631            "███ ▐█              ",
632            "▐▌▐▌ █              ",
633            "▐▌▐▌ █  █ █ ▐█▌     ",
634            "▐██  █  █ █ █ █     ",
635            "▐▌▐▌ █  █ █ ███     ",
636            "▐▌▐▌ █  █ █ █       ",
637            "███ ▐█▌ ▐█▐▌▐█▌     ",
638            "                    ",
639        ]);
640        expected.set_style(Rect::new(0, 0, 12, 8), Style::new().red());
641        expected.set_style(Rect::new(0, 8, 20, 8), Style::new().green());
642        expected.set_style(Rect::new(0, 16, 16, 8), Style::new().blue());
643        assert_eq!(buf, expected);
644    }
645
646    #[test]
647    fn render_quadrant_size_single_line() {
648        let big_text = BigText::builder()
649            .pixel_size(PixelSize::Quadrant)
650            .lines(vec![Line::from("SingleLine")])
651            .build();
652        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 4));
653        big_text.render(buf.area, &mut buf);
654        let expected = Buffer::with_lines(vec![
655            "▟▀▙  ▀          ▝█      ▜▛   ▀          ",
656            "▜▙  ▝█  █▀▙ ▟▀▟▘ █  ▟▀▙ ▐▌  ▝█  █▀▙ ▟▀▙ ",
657            "▄▝█  █  █ █ ▜▄█  █  █▀▀ ▐▌▗▌ █  █ █ █▀▀ ",
658            "▝▀▘ ▝▀▘ ▀ ▀ ▄▄▛ ▝▀▘ ▝▀▘ ▀▀▀▘▝▀▘ ▀ ▀ ▝▀▘ ",
659        ]);
660        assert_eq!(buf, expected);
661    }
662
663    #[test]
664    fn render_quadrant_size_truncated() {
665        let big_text = BigText::builder()
666            .pixel_size(PixelSize::Quadrant)
667            .lines(vec![Line::from("Truncated")])
668            .build();
669        let mut buf = Buffer::empty(Rect::new(0, 0, 35, 3));
670        big_text.render(buf.area, &mut buf);
671        let expected = Buffer::with_lines(vec![
672            "▛█▜                      ▟       ▝█",
673            " █  ▜▟▜▖█ █ █▀▙ ▟▀▙ ▝▀▙ ▝█▀ ▟▀▙ ▗▄█",
674            " █  ▐▌▝▘█ █ █ █ █ ▄ ▟▀█  █▗ █▀▀ █ █",
675        ]);
676        assert_eq!(buf, expected);
677    }
678
679    #[test]
680    fn render_quadrant_size_multiple_lines() {
681        let big_text = BigText::builder()
682            .pixel_size(PixelSize::Quadrant)
683            .lines(vec![Line::from("Multi"), Line::from("Lines")])
684            .build();
685        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 8));
686        big_text.render(buf.area, &mut buf);
687        let expected = Buffer::with_lines(vec![
688            "█▖▟▌    ▝█   ▟   ▀  ",
689            "███▌█ █  █  ▝█▀ ▝█  ",
690            "█▝▐▌█ █  █   █▗  █  ",
691            "▀ ▝▘▝▀▝▘▝▀▘  ▝▘ ▝▀▘ ",
692            "▜▛   ▀              ",
693            "▐▌  ▝█  █▀▙ ▟▀▙ ▟▀▀ ",
694            "▐▌▗▌ █  █ █ █▀▀ ▝▀▙ ",
695            "▀▀▀▘▝▀▘ ▀ ▀ ▝▀▘ ▀▀▘ ",
696        ]);
697        assert_eq!(buf, expected);
698    }
699
700    #[test]
701    fn render_quadrant_size_widget_style() {
702        let big_text = BigText::builder()
703            .pixel_size(PixelSize::Quadrant)
704            .lines(vec![Line::from("Styled")])
705            .style(Style::new().bold())
706            .build();
707        let mut buf = Buffer::empty(Rect::new(0, 0, 24, 4));
708        big_text.render(buf.area, &mut buf);
709        let mut expected = Buffer::with_lines(vec![
710            "▟▀▙  ▟      ▝█       ▝█ ",
711            "▜▙  ▝█▀ █ █  █  ▟▀▙ ▗▄█ ",
712            "▄▝█  █▗ ▜▄█  █  █▀▀ █ █ ",
713            "▝▀▘  ▝▘ ▄▄▛ ▝▀▘ ▝▀▘ ▝▀▝▘",
714        ]);
715        expected.set_style(Rect::new(0, 0, 24, 4), Style::new().bold());
716        assert_eq!(buf, expected);
717    }
718
719    #[test]
720    fn render_quadrant_size_line_style() {
721        let big_text = BigText::builder()
722            .pixel_size(PixelSize::Quadrant)
723            .lines(vec![
724                Line::from("Red".red()),
725                Line::from("Green".green()),
726                Line::from("Blue".blue()),
727            ])
728            .build();
729        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 12));
730        big_text.render(buf.area, &mut buf);
731        let mut expected = Buffer::with_lines(vec![
732            "▜▛▜▖     ▝█         ",
733            "▐▙▟▘▟▀▙ ▗▄█         ",
734            "▐▌▜▖█▀▀ █ █         ",
735            "▀▘▝▘▝▀▘ ▝▀▝▘        ",
736            "▗▛▜▖                ",
737            "█   ▜▟▜▖▟▀▙ ▟▀▙ █▀▙ ",
738            "▜▖▜▌▐▌▝▘█▀▀ █▀▀ █ █ ",
739            " ▀▀▘▀▀  ▝▀▘ ▝▀▘ ▀ ▀ ",
740            "▜▛▜▖▝█              ",
741            "▐▙▟▘ █  █ █ ▟▀▙     ",
742            "▐▌▐▌ █  █ █ █▀▀     ",
743            "▀▀▀ ▝▀▘ ▝▀▝▘▝▀▘     ",
744        ]);
745        expected.set_style(Rect::new(0, 0, 12, 4), Style::new().red());
746        expected.set_style(Rect::new(0, 4, 20, 4), Style::new().green());
747        expected.set_style(Rect::new(0, 8, 16, 4), Style::new().blue());
748        assert_eq!(buf, expected);
749    }
750
751    #[test]
752    fn render_third_height_single_line() {
753        let big_text = BigText::builder()
754            .pixel_size(PixelSize::ThirdHeight)
755            .lines(vec![Line::from("SingleLine")])
756            .build();
757        let mut buf = Buffer::empty(Rect::new(0, 0, 80, 3));
758        big_text.render(buf.area, &mut buf);
759        let expected = Buffer::with_lines(vec![
760            "🬹█🬰🬂🬎🬋   🬭🬰🬰    🬭🬭🬭🬭🬭    🬭🬭🬭 🬭🬭  🬂██     🬭🬭🬭🬭   🬂██🬂     🬭🬰🬰    🬭🬭🬭🬭🬭    🬭🬭🬭🬭   ",
761            "🬭🬰🬂🬎🬹🬹    ██    ██  ██  🬎█🬭🬭██    ██    ██🬋🬋🬎🬎   ██  🬭🬹   ██    ██  ██  ██🬋🬋🬎🬎  ",
762            " 🬂🬂🬂🬂    🬂🬂🬂🬂   🬂🬂  🬂🬂  🬋🬋🬋🬋🬎🬂   🬂🬂🬂🬂    🬂🬂🬂🬂   🬂🬂🬂🬂🬂🬂🬂  🬂🬂🬂🬂   🬂🬂  🬂🬂   🬂🬂🬂🬂   ",
763        ]);
764        assert_eq!(buf, expected);
765    }
766
767    #[test]
768    fn render_third_height_truncated() {
769        let big_text = BigText::builder()
770            .pixel_size(PixelSize::ThirdHeight)
771            .lines(vec![Line::from("Truncated")])
772            .build();
773        let mut buf = Buffer::empty(Rect::new(0, 0, 70, 2));
774        big_text.render(buf.area, &mut buf);
775        let expected = Buffer::with_lines(vec![
776            "🬎🬂██🬂🬎  🬭🬭 🬭🬭🬭  🬭🬭  🬭🬭  🬭🬭🬭🬭🬭    🬭🬭🬭🬭    🬭🬭🬭🬭    🬭🬹█🬭🬭   🬭🬭🬭🬭      🬂██",
777            "  ██     ██🬂 🬎🬎 ██  ██  ██  ██  ██  🬰🬰  🬭🬹🬋🬋██    ██ 🬭  ██🬋🬋🬎🬎  🬹█🬂🬂██",
778        ]);
779        assert_eq!(buf, expected);
780    }
781
782    #[test]
783    fn render_third_height_multiple_lines() {
784        let big_text = BigText::builder()
785            .pixel_size(PixelSize::ThirdHeight)
786            .lines(vec![Line::from("Multi"), Line::from("Lines")])
787            .build();
788        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 6));
789        big_text.render(buf.area, &mut buf);
790        let expected = Buffer::with_lines(vec![
791            "██🬹🬭🬹██ 🬭🬭  🬭🬭   🬂██     🬭🬹█🬭🬭   🬭🬰🬰    ",
792            "██🬂🬎🬂██ ██  ██    ██      ██ 🬭    ██    ",
793            "🬂🬂   🬂🬂  🬂🬂🬂 🬂🬂  🬂🬂🬂🬂      🬂🬂    🬂🬂🬂🬂   ",
794            "🬂██🬂     🬭🬰🬰    🬭🬭🬭🬭🬭    🬭🬭🬭🬭    🬭🬭🬭🬭🬭  ",
795            " ██  🬭🬹   ██    ██  ██  ██🬋🬋🬎🬎  🬂🬎🬋🬋🬹🬭  ",
796            "🬂🬂🬂🬂🬂🬂🬂  🬂🬂🬂🬂   🬂🬂  🬂🬂   🬂🬂🬂🬂   🬂🬂🬂🬂🬂   ",
797        ]);
798        assert_eq!(buf, expected);
799    }
800
801    #[test]
802    fn render_third_height_widget_style() {
803        let big_text = BigText::builder()
804            .pixel_size(PixelSize::ThirdHeight)
805            .lines(vec![Line::from("Styled")])
806            .style(Style::new().bold())
807            .build();
808        let mut buf = Buffer::empty(Rect::new(0, 0, 48, 3));
809        big_text.render(buf.area, &mut buf);
810        let mut expected = Buffer::with_lines(vec![
811            "🬹█🬰🬂🬎🬋   🬭🬹█🬭🬭  🬭🬭  🬭🬭   🬂██     🬭🬭🬭🬭      🬂██  ",
812            "🬭🬰🬂🬎🬹🬹    ██ 🬭  🬎█🬭🬭██    ██    ██🬋🬋🬎🬎  🬹█🬂🬂██  ",
813            " 🬂🬂🬂🬂      🬂🬂   🬋🬋🬋🬋🬎🬂   🬂🬂🬂🬂    🬂🬂🬂🬂    🬂🬂🬂 🬂🬂 ",
814        ]);
815        expected.set_style(Rect::new(0, 0, 48, 3), Style::new().bold());
816        assert_eq!(buf, expected);
817    }
818
819    #[test]
820    fn render_third_height_line_style() {
821        let big_text = BigText::builder()
822            .pixel_size(PixelSize::ThirdHeight)
823            .lines(vec![
824                Line::from("Red".red()),
825                Line::from("Green".green()),
826                Line::from("Blue".blue()),
827            ])
828            .build();
829        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 9));
830        big_text.render(buf.area, &mut buf);
831        let mut expected = Buffer::with_lines(vec![
832            "🬂██🬂🬂█🬹  🬭🬭🬭🬭      🬂██                  ",
833            " ██🬂🬎█🬭 ██🬋🬋🬎🬎  🬹█🬂🬂██                  ",
834            "🬂🬂🬂  🬂🬂  🬂🬂🬂🬂    🬂🬂🬂 🬂🬂                 ",
835            "🬭🬹🬎🬂🬂🬎🬋 🬭🬭 🬭🬭🬭   🬭🬭🬭🬭    🬭🬭🬭🬭   🬭🬭🬭🬭🬭   ",
836            "🬎█🬭 🬋🬹🬹  ██🬂 🬎🬎 ██🬋🬋🬎🬎  ██🬋🬋🬎🬎  ██  ██  ",
837            "  🬂🬂🬂🬂🬂 🬂🬂🬂🬂     🬂🬂🬂🬂    🬂🬂🬂🬂   🬂🬂  🬂🬂  ",
838            "🬂██🬂🬂█🬹  🬂██    🬭🬭  🬭🬭   🬭🬭🬭🬭           ",
839            " ██🬂🬂█🬹   ██    ██  ██  ██🬋🬋🬎🬎          ",
840            "🬂🬂🬂🬂🬂🬂   🬂🬂🬂🬂    🬂🬂🬂 🬂🬂  🬂🬂🬂🬂           ",
841        ]);
842        expected.set_style(Rect::new(0, 0, 24, 3), Style::new().red());
843        expected.set_style(Rect::new(0, 3, 40, 3), Style::new().green());
844        expected.set_style(Rect::new(0, 6, 32, 3), Style::new().blue());
845        assert_eq!(buf, expected);
846    }
847
848    #[test]
849    fn render_sextant_size_single_line() {
850        let big_text = BigText::builder()
851            .pixel_size(PixelSize::Sextant)
852            .lines(vec![Line::from("SingleLine")])
853            .build();
854        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 3));
855        big_text.render(buf.area, &mut buf);
856        let expected = Buffer::with_lines(vec![
857            "🬻🬒🬌 🬞🬰  🬭🬭🬏 🬞🬭🬞🬏🬁█  🬞🬭🬏 🬨🬕  🬞🬰  🬭🬭🬏 🬞🬭🬏 ",
858            "🬯🬊🬹  █  █ █ 🬬🬭█  █  █🬋🬎 ▐▌🬞🬓 █  █ █ █🬋🬎 ",
859            "🬁🬂🬀 🬁🬂🬀 🬂 🬂 🬋🬋🬆 🬁🬂🬀 🬁🬂🬀 🬂🬂🬂🬀🬁🬂🬀 🬂 🬂 🬁🬂🬀 ",
860        ]);
861        assert_eq!(buf, expected);
862    }
863
864    #[test]
865    fn render_sextant_size_truncated() {
866        let big_text = BigText::builder()
867            .pixel_size(PixelSize::Sextant)
868            .lines(vec![Line::from("Truncated")])
869            .build();
870        let mut buf = Buffer::empty(Rect::new(0, 0, 35, 2));
871        big_text.render(buf.area, &mut buf);
872        let expected = Buffer::with_lines(vec![
873            "🬆█🬊 🬭🬞🬭 🬭 🬭 🬭🬭🬏 🬞🬭🬏 🬞🬭🬏 🬞🬻🬭 🬞🬭🬏  🬁█",
874            " █  ▐🬕🬉🬄█ █ █ █ █ 🬰 🬵🬋█  █🬞 █🬋🬎 🬻🬂█",
875        ]);
876        assert_eq!(buf, expected);
877    }
878
879    #[test]
880    fn render_sextant_size_multiple_lines() {
881        let big_text = BigText::builder()
882            .pixel_size(PixelSize::Sextant)
883            .lines(vec![Line::from("Multi"), Line::from("Lines")])
884            .build();
885        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 6));
886        big_text.render(buf.area, &mut buf);
887        let expected = Buffer::with_lines(vec![
888            "█🬱🬻▌🬭 🬭 🬁█  🬞🬻🬭 🬞🬰  ",
889            "█🬊🬨▌█ █  █   █🬞  █  ",
890            "🬂 🬁🬀🬁🬂🬁🬀🬁🬂🬀  🬁🬀 🬁🬂🬀 ",
891            "🬨🬕  🬞🬰  🬭🬭🬏 🬞🬭🬏 🬞🬭🬭 ",
892            "▐▌🬞🬓 █  █ █ █🬋🬎 🬊🬋🬱 ",
893            "🬂🬂🬂🬀🬁🬂🬀 🬂 🬂 🬁🬂🬀 🬂🬂🬀 ",
894        ]);
895        assert_eq!(buf, expected);
896    }
897
898    #[test]
899    fn render_sextant_size_widget_style() {
900        let big_text = BigText::builder()
901            .pixel_size(PixelSize::Sextant)
902            .lines(vec![Line::from("Styled")])
903            .style(Style::new().bold())
904            .build();
905        let mut buf = Buffer::empty(Rect::new(0, 0, 24, 3));
906        big_text.render(buf.area, &mut buf);
907        let mut expected = Buffer::with_lines(vec![
908            "🬻🬒🬌 🬞🬻🬭 🬭 🬭 🬁█  🬞🬭🬏  🬁█ ",
909            "🬯🬊🬹  █🬞 🬬🬭█  █  █🬋🬎 🬻🬂█ ",
910            "🬁🬂🬀  🬁🬀 🬋🬋🬆 🬁🬂🬀 🬁🬂🬀 🬁🬂🬁🬀",
911        ]);
912        expected.set_style(Rect::new(0, 0, 24, 3), Style::new().bold());
913        assert_eq!(buf, expected);
914    }
915
916    #[test]
917    fn render_sextant_size_line_style() {
918        let big_text = BigText::builder()
919            .pixel_size(PixelSize::Sextant)
920            .lines(vec![
921                Line::from("Red".red()),
922                Line::from("Green".green()),
923                Line::from("Blue".blue()),
924            ])
925            .build();
926        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 9));
927        big_text.render(buf.area, &mut buf);
928        let mut expected = Buffer::with_lines(vec![
929            "🬨🬕🬨🬓🬞🬭🬏  🬁█         ",
930            "▐🬕🬬🬏█🬋🬎 🬻🬂█         ",
931            "🬂🬀🬁🬀🬁🬂🬀 🬁🬂🬁🬀        ",
932            "🬵🬆🬊🬃🬭🬞🬭 🬞🬭🬏 🬞🬭🬏 🬭🬭🬏 ",
933            "🬬🬏🬩🬓▐🬕🬉🬄█🬋🬎 █🬋🬎 █ █ ",
934            " 🬂🬂🬀🬂🬂  🬁🬂🬀 🬁🬂🬀 🬂 🬂 ",
935            "🬨🬕🬨🬓🬁█  🬭 🬭 🬞🬭🬏     ",
936            "▐🬕🬨🬓 █  █ █ █🬋🬎     ",
937            "🬂🬂🬂 🬁🬂🬀 🬁🬂🬁🬀🬁🬂🬀     ",
938        ]);
939        expected.set_style(Rect::new(0, 0, 12, 3), Style::new().red());
940        expected.set_style(Rect::new(0, 3, 20, 3), Style::new().green());
941        expected.set_style(Rect::new(0, 6, 16, 3), Style::new().blue());
942        assert_eq!(buf, expected);
943    }
944
945    #[test]
946    fn render_quarter_height_single_line() {
947        let big_text = BigText::builder()
948            .pixel_size(PixelSize::QuarterHeight)
949            .lines(vec![Line::from("SingleLine")])
950            .build();
951        let mut buf = Buffer::empty(Rect::new(0, 0, 80, 2));
952        big_text.render(buf.area, &mut buf);
953        let expected = Buffer::with_lines(vec![
954            "𜴳█𜷝𜶮▀𜴆   𜴧𜷝𜷝    ▄▄𜴧𜴧▄▂  ▂▄𜴧𜴧▂▄𜴧  🮂██    ▂▄𜴧𜴧▄▂  🮂██🮂     𜴧𜷝𜷝    ▄▄𜴧𜴧▄▂  ▂▄𜴧𜴧▄▂  ",
955            "𜴆𜴳𜴧𜴪🮅▀   𜴧🮅🮅𜴧   🮅🮅  🮅🮅  𜶮𜶺𜶷𜶷█🮅   𜴧🮅🮅𜴧   ▀🮅𜴪𜴪𜴪🮂  𜴧🮅🮅𜴧𜴧𜴳🮅  𜴧🮅🮅𜴧   🮅🮅  🮅🮅  ▀🮅𜴪𜴪𜴪🮂  ",
956        ]);
957        assert_eq!(buf, expected);
958    }
959
960    #[test]
961    fn render_quarter_height_truncated() {
962        let big_text = BigText::builder()
963            .pixel_size(PixelSize::QuarterHeight)
964            .lines(vec![Line::from("Truncated")])
965            .build();
966        let mut buf = Buffer::empty(Rect::new(0, 0, 70, 1));
967        big_text.render(buf.area, &mut buf);
968        let expected = Buffer::with_lines(vec![
969            "▀🮂██🮂▀  𜴧▄▂▄𜴧▄▂ ▄▄  ▄▄  ▄▄𜴧𜴧▄▂  ▂▄𜴧𜴧▄▂   𜴧𜴧𜴧▄▂   𜴧▆█𜴧𜴧  ▂▄𜴧𜴧▄▂   ▂▂𜶮██",
970        ]);
971        assert_eq!(buf, expected);
972    }
973
974    #[test]
975    fn render_quarter_height_multiple_lines() {
976        let big_text = BigText::builder()
977            .pixel_size(PixelSize::QuarterHeight)
978            .lines(vec![Line::from("Multi"), Line::from("Lines")])
979            .build();
980        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 4));
981        big_text.render(buf.area, &mut buf);
982        let expected = Buffer::with_lines(vec![
983            "██▆▄▆██ ▄▄  ▄▄   🮂██     𜴧▆█𜴧𜴧   𜴧𜷝𜷝    ",
984            "🮅🮅 🮂 🮅🮅 ▀🮅𜴧𜴧▀🮅𜴧  𜴧🮅🮅𜴧     ▀🮅𜴧𜴆   𜴧🮅🮅𜴧   ",
985            "🮂██🮂     𜴧𜷝𜷝    ▄▄𜴧𜴧▄▂  ▂▄𜴧𜴧▄▂  ▂▄𜴧𜴧𜴧𜴧  ",
986            "𜴧🮅🮅𜴧𜴧𜴳🮅  𜴧🮅🮅𜴧   🮅🮅  🮅🮅  ▀🮅𜴪𜴪𜴪🮂  𜴧𜴪𜴪𜴪🮅𜴆  ",
987        ]);
988        assert_eq!(buf, expected);
989    }
990
991    #[test]
992    fn render_quarter_height_widget_style() {
993        let big_text = BigText::builder()
994            .pixel_size(PixelSize::QuarterHeight)
995            .lines(vec![Line::from("Styled")])
996            .style(Style::new().bold())
997            .build();
998        let mut buf = Buffer::empty(Rect::new(0, 0, 48, 2));
999        big_text.render(buf.area, &mut buf);
1000        let mut expected = Buffer::with_lines(vec![
1001            "𜴳█𜷝𜶮▀𜴆   𜴧▆█𜴧𜴧  ▄▄  ▄▄   🮂██    ▂▄𜴧𜴧▄▂   ▂▂𜶮██  ",
1002            "𜴆𜴳𜴧𜴪🮅▀    ▀🮅𜴧𜴆  𜶮𜶺𜶷𜶷█🮅   𜴧🮅🮅𜴧   ▀🮅𜴪𜴪𜴪🮂  ▀🮅𜴧𜴧▀🮅𜴧 ",
1003        ]);
1004        expected.set_style(Rect::new(0, 0, 48, 2), Style::new().bold());
1005        assert_eq!(buf, expected);
1006    }
1007
1008    #[test]
1009    fn render_quarter_height_line_style() {
1010        let big_text = BigText::builder()
1011            .pixel_size(PixelSize::QuarterHeight)
1012            .lines(vec![
1013                Line::from("Red".red()),
1014                Line::from("Green".green()),
1015                Line::from("Blue".blue()),
1016            ])
1017            .build();
1018        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 6));
1019        big_text.render(buf.area, &mut buf);
1020        let mut expected = Buffer::with_lines(vec![
1021            "🮂██𜶮𜶮█𜴳 ▂▄𜴧𜴧▄▂   ▂▂𜶮██                  ",
1022            "𜴧🮅🮅 🮂🮅𜴳 ▀🮅𜴪𜴪𜴪🮂  ▀🮅𜴧𜴧▀🮅𜴧                 ",
1023            "▄▆▀🮂🮂▀𜴆 𜴧▄▂▄𜴧▄▂ ▂▄𜴧𜴧▄▂  ▂▄𜴧𜴧▄▂  ▄▄𜴧𜴧▄▂  ",
1024            "🮂▀𜴳𜴧𜴪🮅🮅 𜴧🮅🮅𜴧 🮂🮂 ▀🮅𜴪𜴪𜴪🮂  ▀🮅𜴪𜴪𜴪🮂  🮅🮅  🮅🮅  ",
1025            "🮂██𜶮𜶮█𜴳  🮂██    ▄▄  ▄▄  ▂▄𜴧𜴧▄▂          ",
1026            "𜴧🮅🮅𜴧𜴧🮅▀  𜴧🮅🮅𜴧   ▀🮅𜴧𜴧▀🮅𜴧 ▀🮅𜴪𜴪𜴪🮂          ",
1027        ]);
1028        expected.set_style(Rect::new(0, 0, 24, 2), Style::new().red());
1029        expected.set_style(Rect::new(0, 2, 40, 2), Style::new().green());
1030        expected.set_style(Rect::new(0, 4, 32, 2), Style::new().blue());
1031        assert_eq!(buf, expected);
1032    }
1033
1034    #[test]
1035    fn render_octant_size_single_line() {
1036        let big_text = BigText::builder()
1037            .pixel_size(PixelSize::Octant)
1038            .lines(vec![Line::from("SingleLine")])
1039            .build();
1040        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 2));
1041        big_text.render(buf.area, &mut buf);
1042        let expected = Buffer::with_lines(vec![
1043            "𜶪𜶾𜴇 𜴘𜷝  ▄𜴧𜶻 𜷋𜴧𜷋𜴉𜺫█  𜷋𜴧𜶻 𜶘𜵊  𜴘𜷝  ▄𜴧𜶻 𜷋𜴧𜶻 ",
1044            "𜴣𜴩𜴗 𜴘🮅𜴉 🮅 🮅 𜶶𜶷𜵰 𜴘🮅𜴉 𜴦𜴪𜴌 𜴱𜴬𜴯𜴍𜴘🮅𜴉 🮅 🮅 𜴦𜴪𜴌 ",
1045        ]);
1046        assert_eq!(buf, expected);
1047    }
1048
1049    #[test]
1050    #[rustfmt::skip]
1051    fn render_octant_size_truncated() {
1052        let big_text = BigText::builder()
1053            .pixel_size(PixelSize::Octant)
1054            .lines(vec![Line::from("Truncated")])
1055            .build();
1056        let mut buf = Buffer::empty(Rect::new(0, 0, 35, 1));
1057        big_text.render(buf.area, &mut buf);
1058        let expected = Buffer::with_lines(vec![
1059            "𜴂█𜴅 𜶜𜷋𜶜𜺣▄ ▄ ▄𜴧𜶻 𜷋𜴧𜶻 𜴘𜴧𜶻 𜴘𜷥𜴧 𜷋𜴧𜶻 𜺠𜶭█",
1060        ]);
1061        assert_eq!(buf, expected);
1062    }
1063
1064    #[test]
1065    fn render_octant_size_multiple_lines() {
1066        let big_text = BigText::builder()
1067            .pixel_size(PixelSize::Octant)
1068            .lines(vec![Line::from("Multi"), Line::from("Lines")])
1069            .build();
1070        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 4));
1071        big_text.render(buf.area, &mut buf);
1072        let expected = Buffer::with_lines(vec![
1073            "█𜷞𜷥▌▄ ▄ 𜺫█  𜴘𜷥𜴧 𜴘𜷝  ",
1074            "🮅𜺫𜴡𜴍𜴦𜴧𜴦𜴉𜴘🮅𜴉  𜴦𜴐 𜴘🮅𜴉 ",
1075            "𜶘𜵊  𜴘𜷝  ▄𜴧𜶻 𜷋𜴧𜶻 𜷋𜴧𜴧 ",
1076            "𜴱𜴬𜴯𜴍𜴘🮅𜴉 🮅 🮅 𜴦𜴪𜴌 𜴩𜴪𜴕 ",
1077        ]);
1078        assert_eq!(buf, expected);
1079    }
1080
1081    #[test]
1082    #[rustfmt::skip]
1083    fn render_octant_size_widget_style() {
1084        let big_text = BigText::builder()
1085            .pixel_size(PixelSize::Octant)
1086            .lines(vec![Line::from("Styled")])
1087            .style(Style::new().bold())
1088            .build();
1089        let mut buf = Buffer::empty(Rect::new(0, 0, 24, 2));
1090        big_text.render(buf.area, &mut buf);
1091        let mut expected = Buffer::with_lines(vec![
1092            "𜶪𜶾𜴇 𜴘𜷥𜴧 ▄ ▄ 𜺫█  𜷋𜴧𜶻 𜺠𜶭█ ",
1093            "𜴣𜴩𜴗  𜴦𜴐 𜶶𜶷𜵰 𜴘🮅𜴉 𜴦𜴪𜴌 𜴦𜴧𜴦𜴉",
1094        ]);
1095        expected.set_style(Rect::new(0, 0, 24, 2), Style::new().bold());
1096        assert_eq!(buf, expected);
1097    }
1098
1099    #[test]
1100    fn render_octant_size_line_style() {
1101        let big_text = BigText::builder()
1102            .pixel_size(PixelSize::Octant)
1103            .lines(vec![
1104                Line::from("Red".red()),
1105                Line::from("Green".green()),
1106                Line::from("Blue".blue()),
1107            ])
1108            .build();
1109        let mut buf = Buffer::empty(Rect::new(0, 0, 20, 6));
1110        big_text.render(buf.area, &mut buf);
1111        let mut expected = Buffer::with_lines(vec![
1112            "𜶘𜷂𜷖🯦𜷋𜴧𜶻 𜺠𜶭█         ",
1113            "𜴱𜴍𜴢🯦𜴦𜴪𜴌 𜴦𜴧𜴦𜴉        ",
1114            "𜷡𜴂𜴅𜴀𜶜𜷋𜶜𜺣𜷋𜴧𜶻 𜷋𜴧𜶻 ▄𜴧𜶻 ",
1115            "𜴅𜴫𜴲𜴍𜴱𜴬𜺫𜺨𜴦𜴪𜴌 𜴦𜴪𜴌 🮅 🮅 ",
1116            "𜶘𜷂𜷖🯦𜺫█  ▄ ▄ 𜷋𜴧𜶻     ",
1117            "𜴱𜴬𜴱▘𜴘🮅𜴉 𜴦𜴧𜴦𜴉𜴦𜴪𜴌     ",
1118        ]);
1119        expected.set_style(Rect::new(0, 0, 12, 2), Style::new().red());
1120        expected.set_style(Rect::new(0, 2, 20, 2), Style::new().green());
1121        expected.set_style(Rect::new(0, 4, 16, 2), Style::new().blue());
1122        assert_eq!(buf, expected);
1123    }
1124
1125    #[test]
1126    fn render_alignment_left() {
1127        let big_text = BigText::builder()
1128            .pixel_size(PixelSize::Quadrant)
1129            .lines(vec![Line::from("Left")])
1130            .alignment(Alignment::Left)
1131            .build();
1132        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 4));
1133        big_text.render(buf.area, &mut buf);
1134        let expected = Buffer::with_lines(vec![
1135            "▜▛      ▗▛▙  ▟                          ",
1136            "▐▌  ▟▀▙ ▟▙  ▝█▀                         ",
1137            "▐▌▗▌█▀▀ ▐▌   █▗                         ",
1138            "▀▀▀▘▝▀▘ ▀▀   ▝▘                         ",
1139        ]);
1140        assert_eq!(buf, expected);
1141    }
1142
1143    #[test]
1144    fn render_alignment_right() {
1145        let big_text = BigText::builder()
1146            .pixel_size(PixelSize::Quadrant)
1147            .lines(vec![Line::from("Right")])
1148            .alignment(Alignment::Right)
1149            .build();
1150        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 4));
1151        big_text.render(buf.area, &mut buf);
1152        let expected = Buffer::with_lines(vec![
1153            "                    ▜▛▜▖ ▀      ▜▌   ▟  ",
1154            "                    ▐▙▟▘▝█  ▟▀▟▘▐▙▜▖▝█▀ ",
1155            "                    ▐▌▜▖ █  ▜▄█ ▐▌▐▌ █▗ ",
1156            "                    ▀▘▝▘▝▀▘ ▄▄▛ ▀▘▝▘ ▝▘ ",
1157        ]);
1158        assert_eq!(buf, expected);
1159    }
1160
1161    #[test]
1162    fn render_alignment_center() {
1163        let big_text = BigText::builder()
1164            .pixel_size(PixelSize::Quadrant)
1165            .lines(vec![Line::from("Centered"), Line::from("Lines")])
1166            .alignment(Alignment::Center)
1167            .build();
1168        let mut buf = Buffer::empty(Rect::new(0, 0, 40, 8));
1169        big_text.render(buf.area, &mut buf);
1170        let expected = Buffer::with_lines(vec![
1171            "    ▗▛▜▖         ▟               ▝█     ",
1172            "    █   ▟▀▙ █▀▙ ▝█▀ ▟▀▙ ▜▟▜▖▟▀▙ ▗▄█     ",
1173            "    ▜▖▗▖█▀▀ █ █  █▗ █▀▀ ▐▌▝▘█▀▀ █ █     ",
1174            "     ▀▀ ▝▀▘ ▀ ▀  ▝▘ ▝▀▘ ▀▀  ▝▀▘ ▝▀▝▘    ",
1175            "          ▜▛   ▀                        ",
1176            "          ▐▌  ▝█  █▀▙ ▟▀▙ ▟▀▀           ",
1177            "          ▐▌▗▌ █  █ █ █▀▀ ▝▀▙           ",
1178            "          ▀▀▀▘▝▀▘ ▀ ▀ ▝▀▘ ▀▀▘           ",
1179        ]);
1180        assert_eq!(buf, expected);
1181    }
1182
1183    #[test]
1184    fn render_hiragana_font() {
1185        let big_text = BigText::builder()
1186            .lines(vec![Line::from("ひらがな")])
1187            .build();
1188        let mut buf = Buffer::empty(Rect::new(0, 0, 32, 8));
1189
1190        big_text.render(buf.area, &mut buf);
1191
1192        let expected = Buffer::with_lines(vec![
1193            "         ████     █   █  █  ██  ",
1194            "                  █  █   █      ",
1195            "██  █    █      ████    ███  █  ",
1196            " █  ██   █ ███    █ █ █  █   █  ",
1197            "█   █ █  ██   █  █  █ █  █  ███ ",
1198            "█   █    █    █  █  █ █ █  █ █  ",
1199            " ███        ██  █  █    █   █   ",
1200            "                                ",
1201        ]);
1202        assert_eq!(buf, expected);
1203    }
1204
1205    #[test]
1206    fn render_greek_font() {
1207        let big_text = BigText::builder()
1208            .lines(vec![Line::from("ελληνικά")])
1209            .build();
1210        let mut buf = Buffer::empty(Rect::new(0, 0, 64, 8));
1211
1212        big_text.render(buf.area, &mut buf);
1213
1214        let expected = Buffer::with_lines(vec![
1215            "                                                            ███ ",
1216            "        ██      ██                                              ",
1217            " ████    ██      ██     █████   ██  ██    ██    ██  ██   ███ ██ ",
1218            "██        ██      ██    ██  ██  ██  ██    ██    ██ ██   ██ ███  ",
1219            " ███      ███     ███   ██  ██  ██  ██    ██    ████    ██  █   ",
1220            "██       ██ ██   ██ ██  ██  ██   ████     ██ █  ██ ██   ██ ███  ",
1221            " ████   ██   ██ ██   ██ ██  ██    ██       ██   ██  ██   ███ ██ ",
1222            "                            ██                                  ",
1223        ]);
1224        assert_eq!(buf, expected);
1225    }
1226
1227    #[test]
1228    fn centered_hiragana_uses_glyph_count_for_alignment() {
1229        let line = Line::from("ひらがな");
1230
1231        assert_eq!(get_alignment_offset(80, 8, Alignment::Center, &line), 24);
1232    }
1233
1234    #[test]
1235    fn alignment_width_clamps_to_u16_max() {
1236        let line = Line::from("x".repeat(usize::from(u16::MAX) + 1));
1237
1238        assert_eq!(get_alignment_offset(80, 1, Alignment::Center, &line), 0);
1239    }
1240
1241    #[test]
1242    fn render_with_block() {
1243        let big_text = BigText::builder()
1244            .lines(vec![Line::from("In Block")])
1245            .block(Block::bordered().title("Big Text Block"))
1246            .build();
1247        let mut buf = Buffer::empty(Rect::new(0, 0, 65, 10));
1248        big_text.render(buf.area, &mut buf);
1249        let expected = Buffer::with_lines(vec![
1250            "┌Big Text Block─────────────────────────────────────────────────┐",
1251            "│ ████                   ██████   ███                    ███    │",
1252            "│  ██                     ██  ██   ██                     ██    │",
1253            "│  ██    █████            ██  ██   ██     ████    ████    ██  ██│",
1254            "│  ██    ██  ██           █████    ██    ██  ██  ██  ██   ██ ██ │",
1255            "│  ██    ██  ██           ██  ██   ██    ██  ██  ██       ████  │",
1256            "│  ██    ██  ██           ██  ██   ██    ██  ██  ██  ██   ██ ██ │",
1257            "│ ████   ██  ██          ██████   ████    ████    ████   ███  ██│",
1258            "│                                                               │",
1259            "└───────────────────────────────────────────────────────────────┘",
1260        ]);
1261        assert_eq!(buf, expected);
1262    }
1263}