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