pdfgen/types/hierarchy/primitives/
rectangle.rs

1use std::io::{Error, Write};
2
3use super::unit::Unit;
4
5/// Represents a point (pair of x and y coordinates) in default user space units.
6#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
7pub struct Position {
8    /// X coordinate in user space unit.
9    pub x: Unit,
10
11    /// y coordinate in user space unit.
12    pub y: Unit,
13}
14
15impl Position {
16    pub const fn new(x: Unit, y: Unit) -> Self {
17        Self { x, y }
18    }
19
20    pub const fn from_mm(x: f32, y: f32) -> Self {
21        Self {
22            x: Unit::from_mm(x),
23            y: Unit::from_mm(y),
24        }
25    }
26
27    pub const fn from_units(x: f32, y: f32) -> Position {
28        Self {
29            x: Unit::from_unit(x),
30            y: Unit::from_unit(y),
31        }
32    }
33}
34
35/// Rectangles are used to describe locations on a page and bounding boxes for a variety of
36/// objects. A rectangle shall be written as an array of four numbers giving the coordinates of a
37/// pair of diagonally opposite corners.
38#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
39pub struct Rectangle {
40    /// Lower left corner of the `Rectangle`.
41    low_left: Position,
42
43    /// Upper right corner of the `Rectangle`.
44    top_right: Position,
45}
46
47macro_rules! gen_page_constants {
48    ($($name:ident, $width:literal, $height:literal),* $(,)?) => {
49        $(
50        pub const $name: Self = Self::new(Position::from_mm(0.0, 0.0), Position::from_mm($width - 1.0, $height - 1.0));
51        )*
52    }
53}
54
55impl Rectangle {
56    gen_page_constants! {
57        A0_4, 1682.0, 2378.0,
58        A0_2, 1189.0, 1682.0,
59          A0, 841.0, 1189.0,
60          A1, 594.0, 841.0,
61          A2, 420.0, 594.0,
62          A3, 297.0, 420.0,
63          A4, 210.0, 297.0,
64          A5, 148.0, 210.0,
65          A6, 105.0, 148.0,
66          A7, 74.0, 105.0,
67          A8, 52.0, 74.0,
68          A9, 37.0, 52.0,
69         A10, 26.0, 37.0,
70    }
71
72    /// Create a new [`Rectangle`] with given [`Position`]s as its corners.
73    pub const fn new(low_left: Position, top_right: Position) -> Self {
74        Self {
75            low_left,
76            top_right,
77        }
78    }
79
80    /// Encode and write this [`Rectangle`] into the provided implementor of [`Write`].
81    pub fn write(&self, writer: &mut dyn Write) -> Result<usize, Error> {
82        let output = format!(
83            "[{} {} {} {}]",
84            self.low_left.x, self.low_left.y, self.top_right.x, self.top_right.y
85        );
86        writer.write(output.as_bytes())
87    }
88
89    pub fn from_units(ll_x: f32, ll_y: f32, tr_x: f32, tr_y: f32) -> Self {
90        Self {
91            low_left: Position::from_units(ll_x, ll_y),
92            top_right: Position::from_units(tr_x, tr_y),
93        }
94    }
95
96    /// Returns the width of this `Rectangle`.
97    pub fn width(&self) -> Unit {
98        self.top_right.x - self.low_left.x
99    }
100
101    /// Returns the height of this `Rectangle`.
102    pub fn height(&self) -> Unit {
103        self.top_right.y - self.low_left.y
104    }
105}
106
107impl From<(u32, u32, u32, u32)> for Rectangle {
108    fn from((ll_x, ll_y, tr_x, tr_y): (u32, u32, u32, u32)) -> Self {
109        Self::from((ll_x as f32, ll_y as f32, tr_x as f32, tr_y as f32))
110    }
111}
112
113impl From<(f32, f32, f32, f32)> for Rectangle {
114    fn from((ll_x, ll_y, tr_x, tr_y): (f32, f32, f32, f32)) -> Self {
115        Self {
116            low_left: Position::from_mm(ll_x, ll_y),
117            top_right: Position::from_mm(tr_x, tr_y),
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use crate::types::hierarchy::primitives::unit::Unit;
125
126    use super::Rectangle;
127
128    #[test]
129    fn new_rectangle() {
130        let rect = Rectangle::from((24, 25, 42, 43));
131
132        assert_eq!(rect.low_left.x, Unit::from_mm(24.0));
133        assert_eq!(rect.low_left.y, Unit::from_mm(25.0));
134        assert_eq!(rect.top_right.x, Unit::from_mm(42.0));
135        assert_eq!(rect.top_right.y, Unit::from_mm(43.0));
136    }
137
138    #[test]
139    fn output() {
140        let rect = Rectangle::from_units(24.0, 25.0, 42.0, 43.0);
141
142        let mut output = Vec::new();
143        rect.write(&mut output).unwrap();
144        let output = String::from_utf8(output).unwrap();
145
146        insta::assert_snapshot!(output, @"[24 25 42 43]");
147    }
148}