Skip to main content

laser_pdf/test_utils/
fake_text.rs

1use super::*;
2
3/// A predictable element for testing containers. It's a bit simpler than actual text in that it
4/// doesn't vary it's height based on input width. It just either returns the width from the
5/// constraint or [Self::width] if unconstrained.
6pub struct FakeText {
7    pub lines: u32,
8    pub line_height: f32,
9    pub width: f32,
10}
11
12struct Layout {
13    first_lines: u32,
14    full_height_lines: u32,
15    lines: u32,
16    breaks: u32,
17}
18
19impl FakeText {
20    fn lines_and_breaks(&self, first_height: f32, full_height: f32) -> Layout {
21        let first_lines = (first_height / self.line_height).floor() as u32;
22
23        if self.lines <= first_lines {
24            Layout {
25                first_lines: self.lines,
26                full_height_lines: 0,
27                lines: self.lines,
28                breaks: 0,
29            }
30        } else {
31            let remaining_lines = self.lines - first_lines;
32            let lines_per_page = ((full_height / self.line_height).floor() as u32).max(1);
33            let full_pages = remaining_lines / lines_per_page;
34            let last_page_lines = remaining_lines % lines_per_page;
35
36            Layout {
37                first_lines,
38                full_height_lines: lines_per_page,
39                lines: if last_page_lines == 0 {
40                    lines_per_page
41                } else {
42                    last_page_lines
43                },
44                breaks: full_pages + if last_page_lines == 0 { 0 } else { 1 },
45            }
46        }
47    }
48}
49
50impl Element for FakeText {
51    fn first_location_usage(&self, ctx: FirstLocationUsageCtx) -> FirstLocationUsage {
52        if ctx.first_height < self.line_height {
53            FirstLocationUsage::WillSkip
54        } else {
55            FirstLocationUsage::WillUse
56        }
57    }
58
59    fn measure(&self, ctx: MeasureCtx) -> ElementSize {
60        let lines = if let Some(breakable) = ctx.breakable {
61            let layout = self.lines_and_breaks(ctx.first_height, breakable.full_height);
62
63            *breakable.break_count = layout.breaks;
64            layout.lines
65        } else {
66            self.lines
67        };
68
69        ElementSize {
70            width: Some(ctx.width.constrain(self.width)),
71            height: Some(lines as f32 * self.line_height),
72        }
73    }
74
75    fn draw(&self, ctx: DrawCtx) -> ElementSize {
76        let lines = if let Some(breakable) = ctx.breakable {
77            let layout = self.lines_and_breaks(ctx.first_height, breakable.full_height);
78
79            for i in 0..layout.breaks {
80                (breakable.do_break)(
81                    ctx.pdf,
82                    i,
83                    Some(if i == 0 {
84                        self.line_height * layout.first_lines as f32
85                    } else {
86                        self.line_height * layout.full_height_lines as f32
87                    }),
88                );
89            }
90
91            layout.lines
92        } else {
93            self.lines
94        };
95
96        ElementSize {
97            width: Some(ctx.width.constrain(self.width)),
98            height: Some(lines as f32 * self.line_height),
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_fake_text() {
109        let element = FakeText {
110            line_height: 1.,
111            lines: 11,
112            width: 5.,
113        };
114
115        for mut output in (ElementTestParams {
116            first_height: 1.999,
117            full_height: 3.3,
118            ..Default::default()
119        })
120        .run(&element)
121        {
122            if let Some(ref mut b) = output.breakable {
123                b.assert_break_count(if output.first_height == 1.999 { 4 } else { 3 });
124            }
125
126            output.assert_size(ElementSize {
127                width: Some(if output.width.expand {
128                    output.width.max
129                } else {
130                    5.
131                }),
132
133                height: Some(if output.breakable.is_some() {
134                    if output.first_height == 1.999 {
135                        1.
136                    } else {
137                        2.
138                    }
139                } else {
140                    11.
141                }),
142            });
143        }
144    }
145}