1from typing import Any, Union
2
3from ._base import FloatObject, NumberObject
4from ._data_structures import ArrayObject
5
6
7class RectangleObject(ArrayObject):
8 """
9 This class is used to represent *page boxes* in pypdf.
10
11 These boxes include:
12
13 * :attr:`artbox <pypdf._page.PageObject.artbox>`
14 * :attr:`bleedbox <pypdf._page.PageObject.bleedbox>`
15 * :attr:`cropbox <pypdf._page.PageObject.cropbox>`
16 * :attr:`mediabox <pypdf._page.PageObject.mediabox>`
17 * :attr:`trimbox <pypdf._page.PageObject.trimbox>`
18 """
19
20 def __init__(
21 self, arr: Union["RectangleObject", tuple[float, float, float, float]]
22 ) -> None:
23 # must have four points
24 assert len(arr) == 4
25 # automatically convert arr[x] into NumberObject(arr[x]) if necessary
26 ArrayObject.__init__(self, [self._ensure_is_number(x) for x in arr])
27
28 def _ensure_is_number(self, value: Any) -> Union[FloatObject, NumberObject]:
29 if not isinstance(value, (FloatObject, NumberObject)):
30 return FloatObject(value)
31 return value
32
33 def scale(self, sx: float, sy: float) -> "RectangleObject":
34 return RectangleObject(
35 (
36 float(self.left) * sx,
37 float(self.bottom) * sy,
38 float(self.right) * sx,
39 float(self.top) * sy,
40 )
41 )
42
43 def __repr__(self) -> str:
44 return f"RectangleObject({list(self)!r})"
45
46 @property
47 def left(self) -> FloatObject:
48 value: FloatObject = self[0]
49 return value
50
51 @left.setter
52 def left(self, f: float) -> None:
53 self[0] = FloatObject(f)
54
55 @property
56 def bottom(self) -> FloatObject:
57 value: FloatObject = self[1]
58 return value
59
60 @bottom.setter
61 def bottom(self, f: float) -> None:
62 self[1] = FloatObject(f)
63
64 @property
65 def right(self) -> FloatObject:
66 value: FloatObject = self[2]
67 return value
68
69 @right.setter
70 def right(self, f: float) -> None:
71 self[2] = FloatObject(f)
72
73 @property
74 def top(self) -> FloatObject:
75 value: FloatObject = self[3]
76 return value
77
78 @top.setter
79 def top(self, f: float) -> None:
80 self[3] = FloatObject(f)
81
82 @property
83 def lower_left(self) -> tuple[float, float]:
84 """
85 Property to read and modify the lower left coordinate of this box
86 in (x,y) form.
87 """
88 return self.left, self.bottom
89
90 @lower_left.setter
91 def lower_left(self, value: tuple[float, float]) -> None:
92 self[0], self[1] = (self._ensure_is_number(x) for x in value)
93
94 @property
95 def lower_right(self) -> tuple[float, float]:
96 """
97 Property to read and modify the lower right coordinate of this box
98 in (x,y) form.
99 """
100 return self.right, self.bottom
101
102 @lower_right.setter
103 def lower_right(self, value: tuple[float, float]) -> None:
104 self[2], self[1] = (self._ensure_is_number(x) for x in value)
105
106 @property
107 def upper_left(self) -> tuple[float, float]:
108 """
109 Property to read and modify the upper left coordinate of this box
110 in (x,y) form.
111 """
112 return self.left, self.top
113
114 @upper_left.setter
115 def upper_left(self, value: tuple[float, float]) -> None:
116 self[0], self[3] = (self._ensure_is_number(x) for x in value)
117
118 @property
119 def upper_right(self) -> tuple[float, float]:
120 """
121 Property to read and modify the upper right coordinate of this box
122 in (x,y) form.
123 """
124 return self.right, self.top
125
126 @upper_right.setter
127 def upper_right(self, value: tuple[float, float]) -> None:
128 self[2], self[3] = (self._ensure_is_number(x) for x in value)
129
130 @property
131 def width(self) -> float:
132 return self.right - self.left
133
134 @property
135 def height(self) -> float:
136 return self.top - self.bottom