Immutable value object representing an image such as a JPEG, PNG, or GIF.
| 140 | |
| 141 | |
| 142 | class Image(object): |
| 143 | """Immutable value object representing an image such as a JPEG, PNG, or GIF.""" |
| 144 | |
| 145 | def __init__(self, blob: bytes, filename: str | None): |
| 146 | super(Image, self).__init__() |
| 147 | self._blob = blob |
| 148 | self._filename = filename |
| 149 | |
| 150 | @classmethod |
| 151 | def from_blob(cls, blob: bytes, filename: str | None = None) -> Image: |
| 152 | """Return a new |Image| object loaded from the image binary in `blob`.""" |
| 153 | return cls(blob, filename) |
| 154 | |
| 155 | @classmethod |
| 156 | def from_file(cls, image_file: str | IO[bytes]) -> Image: |
| 157 | """Return a new |Image| object loaded from `image_file`. |
| 158 | |
| 159 | `image_file` can be either a path (str) or a file-like object. |
| 160 | """ |
| 161 | if isinstance(image_file, str): |
| 162 | # treat image_file as a path |
| 163 | with open(image_file, "rb") as f: |
| 164 | blob = f.read() |
| 165 | filename = os.path.basename(image_file) |
| 166 | else: |
| 167 | # assume image_file is a file-like object |
| 168 | # ---reposition file cursor if it has one--- |
| 169 | if callable(getattr(image_file, "seek")): |
| 170 | image_file.seek(0) |
| 171 | blob = image_file.read() |
| 172 | filename = None |
| 173 | |
| 174 | return cls.from_blob(blob, filename) |
| 175 | |
| 176 | @property |
| 177 | def blob(self) -> bytes: |
| 178 | """The binary image bytestream of this image.""" |
| 179 | return self._blob |
| 180 | |
| 181 | @lazyproperty |
| 182 | def content_type(self) -> str: |
| 183 | """MIME-type of this image, e.g. `"image/jpeg"`.""" |
| 184 | return image_content_types[self.ext] |
| 185 | |
| 186 | @lazyproperty |
| 187 | def dpi(self) -> tuple[int, int]: |
| 188 | """A (horz_dpi, vert_dpi) 2-tuple specifying the dots-per-inch resolution of this image. |
| 189 | |
| 190 | A default value of (72, 72) is used if the dpi is not specified in the image file. |
| 191 | """ |
| 192 | |
| 193 | def int_dpi(dpi: Any): |
| 194 | """Return an integer dots-per-inch value corresponding to `dpi`. |
| 195 | |
| 196 | If `dpi` is |None|, a non-numeric type, less than 1 or greater than 2048, 72 is |
| 197 | returned. |
| 198 | """ |
| 199 | try: |
no outgoing calls
searching dependent graphs…