nem sei pq tantos arquivos

This commit is contained in:
2025-02-11 11:07:58 -03:00
parent 66fb4eb17b
commit 2da09a8a25
1841 changed files with 115867 additions and 77478 deletions

View File

@@ -43,7 +43,7 @@
<tr> <tr>
<td>{{item.product.name}}</td> <td>{{item.product.name}}</td>
<td>R$ {{item.product.price}}</td> <td>R$ {{item.product.price}} </td>
<td><button class="btn-cancel" onclick="removeProductBalcao({{item.id}})">🗑️ Excluir</button></td> <td><button class="btn-cancel" onclick="removeProductBalcao({{item.id}})">🗑️ Excluir</button></td>
</tr> </tr>

Binary file not shown.

View File

@@ -41,6 +41,7 @@ function imprimirFichas() {
content = content.replace(/<button[^>]*>(?:(?!<\/button>)[\s\S])*<\/button>/gi,''); content = content.replace(/<button[^>]*>(?:(?!<\/button>)[\s\S])*<\/button>/gi,'');
content = content.replace(/<tfoot[^>]*>(?:(?!<\/tfoot>)[\s\S])*<\/tfoot>/gi,''); content = content.replace(/<tfoot[^>]*>(?:(?!<\/tfoot>)[\s\S])*<\/tfoot>/gi,'');
content = content.replace(/<th[^>]*>(?:(?!<\/th>)[\s\S])*<\/th>/gi,''); content = content.replace(/<th[^>]*>(?:(?!<\/th>)[\s\S])*<\/th>/gi,'');
content = content.replace(/R\$.{7}/g, '');
content = content.replace(/<\/tr>/g,'</tr><tr><td colspan="2" style="font-size: 12px">'+dateString+ '<BR>VÁLIDO SOMENTE POR ESSA NOITE'+'</td></tr>'); content = content.replace(/<\/tr>/g,'</tr><tr><td colspan="2" style="font-size: 12px">'+dateString+ '<BR>VÁLIDO SOMENTE POR ESSA NOITE'+'</td></tr>');
var printWindow = window.open('', '_blank'); var printWindow = window.open('', '_blank');

View File

@@ -0,0 +1,28 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,92 @@
Metadata-Version: 2.1
Name: MarkupSafe
Version: 3.0.2
Summary: Safely add untrusted strings to HTML/XML markup.
Maintainer-email: Pallets <contact@palletsprojects.com>
License: Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
Project-URL: Source, https://github.com/pallets/markupsafe/
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
# MarkupSafe
MarkupSafe implements a text object that escapes characters so it is
safe to use in HTML and XML. Characters that have special meanings are
replaced so that they display as the actual characters. This mitigates
injection attacks, meaning untrusted user input can safely be displayed
on a page.
## Examples
```pycon
>>> from markupsafe import Markup, escape
>>> # escape replaces special characters and wraps in Markup
>>> escape("<script>alert(document.cookie);</script>")
Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
>>> # wrap in Markup to mark text "safe" and prevent escaping
>>> Markup("<strong>Hello</strong>")
Markup('<strong>hello</strong>')
>>> escape(Markup("<strong>Hello</strong>"))
Markup('<strong>hello</strong>')
>>> # Markup is a str subclass
>>> # methods and operators escape their arguments
>>> template = Markup("Hello <em>{name}</em>")
>>> template.format(name='"World"')
Markup('Hello <em>&#34;World&#34;</em>')
```
## Donate
The Pallets organization develops and supports MarkupSafe and other
popular packages. In order to grow the community of contributors and
users, and allow the maintainers to devote more time to the projects,
[please donate today][].
[please donate today]: https://palletsprojects.com/donate

View File

@@ -0,0 +1,15 @@
MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503
MarkupSafe-3.0.2.dist-info/METADATA,sha256=nhoabjupBG41j_JxPCJ3ylgrZ6Fx8oMCFbiLF9Kafqc,4067
MarkupSafe-3.0.2.dist-info/RECORD,,
MarkupSafe-3.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
MarkupSafe-3.0.2.dist-info/WHEEL,sha256=IqiWNwTSPPvorR7mTezuRY2eqj__44JKKkjOiewDX64,101
MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
markupsafe/__init__.py,sha256=pREerPwvinB62tNCMOwqxBS2YHV6R52Wcq1d-rB4Z5o,13609
markupsafe/__pycache__/__init__.cpython-310.pyc,,
markupsafe/__pycache__/_native.cpython-310.pyc,,
markupsafe/_native.py,sha256=2ptkJ40yCcp9kq3L1NqpgjfpZB-obniYKFFKUOkHh4Q,218
markupsafe/_speedups.c,sha256=SglUjn40ti9YgQAO--OgkSyv9tXq9vvaHyVhQows4Ok,4353
markupsafe/_speedups.cp310-win_amd64.pyd,sha256=RTvh-UzJTX7J_4j-A5jZmnqwRKBe0pQiDPd_j60jft8,13312
markupsafe/_speedups.pyi,sha256=LSDmXYOefH4HVpAXuL8sl7AttLw0oXh1njVoVZp2wqQ,42
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

View File

@@ -1,5 +1,5 @@
Wheel-Version: 1.0 Wheel-Version: 1.0
Generator: setuptools (75.1.0) Generator: setuptools (75.2.0)
Root-Is-Purelib: false Root-Is-Purelib: false
Tag: cp310-cp310-win_amd64 Tag: cp310-cp310-win_amd64

View File

@@ -0,0 +1 @@
markupsafe

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
# Copyright (C) AB Strakt
# See LICENSE for details.
"""
pyOpenSSL - A simple wrapper around the OpenSSL library
"""
from OpenSSL import SSL, crypto
from OpenSSL.version import (
__author__,
__copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
)
__all__ = [
"SSL",
"__author__",
"__copyright__",
"__email__",
"__license__",
"__summary__",
"__title__",
"__uri__",
"__version__",
"crypto",
]

View File

@@ -0,0 +1,126 @@
from __future__ import annotations
import os
import sys
import warnings
from typing import Any, Callable, NoReturn, Union
from cryptography.hazmat.bindings.openssl.binding import Binding
StrOrBytesPath = Union[str, bytes, os.PathLike]
binding = Binding()
ffi = binding.ffi
lib = binding.lib
# This is a special CFFI allocator that does not bother to zero its memory
# after allocation. This has vastly better performance on large allocations and
# so should be used whenever we don't need the memory zeroed out.
no_zero_allocator = ffi.new_allocator(should_clear_after_alloc=False)
def text(charp: Any) -> str:
"""
Get a native string type representing of the given CFFI ``char*`` object.
:param charp: A C-style string represented using CFFI.
:return: :class:`str`
"""
if not charp:
return ""
return ffi.string(charp).decode("utf-8")
def exception_from_error_queue(exception_type: type[Exception]) -> NoReturn:
"""
Convert an OpenSSL library failure into a Python exception.
When a call to the native OpenSSL library fails, this is usually signalled
by the return value, and an error code is stored in an error queue
associated with the current thread. The err library provides functions to
obtain these error codes and textual error messages.
"""
errors = []
while True:
error = lib.ERR_get_error()
if error == 0:
break
errors.append(
(
text(lib.ERR_lib_error_string(error)),
text(lib.ERR_func_error_string(error)),
text(lib.ERR_reason_error_string(error)),
)
)
raise exception_type(errors)
def make_assert(error: type[Exception]) -> Callable[[bool], Any]:
"""
Create an assert function that uses :func:`exception_from_error_queue` to
raise an exception wrapped by *error*.
"""
def openssl_assert(ok: bool) -> None:
"""
If *ok* is not True, retrieve the error from OpenSSL and raise it.
"""
if ok is not True:
exception_from_error_queue(error)
return openssl_assert
def path_bytes(s: StrOrBytesPath) -> bytes:
"""
Convert a Python path to a :py:class:`bytes` for the path which can be
passed into an OpenSSL API accepting a filename.
:param s: A path (valid for os.fspath).
:return: An instance of :py:class:`bytes`.
"""
b = os.fspath(s)
if isinstance(b, str):
return b.encode(sys.getfilesystemencoding())
else:
return b
def byte_string(s: str) -> bytes:
return s.encode("charmap")
# A marker object to observe whether some optional arguments are passed any
# value or not.
UNSPECIFIED = object()
_TEXT_WARNING = "str for {0} is no longer accepted, use bytes"
def text_to_bytes_and_warn(label: str, obj: Any) -> Any:
"""
If ``obj`` is text, emit a warning that it should be bytes instead and try
to convert it to bytes automatically.
:param str label: The name of the parameter from which ``obj`` was taken
(so a developer can easily find the source of the problem and correct
it).
:return: If ``obj`` is the text string type, a ``bytes`` object giving the
UTF-8 encoding of that text is returned. Otherwise, ``obj`` itself is
returned.
"""
if isinstance(obj, str):
warnings.warn(
_TEXT_WARNING.format(label),
category=DeprecationWarning,
stacklevel=3,
)
return obj.encode("utf-8")
return obj

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
import ssl
import sys
import cffi
import cryptography
import OpenSSL.SSL
from . import version
_env_info = """\
pyOpenSSL: {pyopenssl}
cryptography: {cryptography}
cffi: {cffi}
cryptography's compiled against OpenSSL: {crypto_openssl_compile}
cryptography's linked OpenSSL: {crypto_openssl_link}
Python's OpenSSL: {python_openssl}
Python executable: {python}
Python version: {python_version}
Platform: {platform}
sys.path: {sys_path}""".format(
pyopenssl=version.__version__,
crypto_openssl_compile=OpenSSL._util.ffi.string(
OpenSSL._util.lib.OPENSSL_VERSION_TEXT,
).decode("ascii"),
crypto_openssl_link=OpenSSL.SSL.SSLeay_version(
OpenSSL.SSL.SSLEAY_VERSION
).decode("ascii"),
python_openssl=getattr(ssl, "OPENSSL_VERSION", "n/a"),
cryptography=cryptography.__version__,
cffi=cffi.__version__,
python=sys.executable,
python_version=sys.version,
platform=sys.platform,
sys_path=sys.path,
)
if __name__ == "__main__":
print(_env_info)

View File

@@ -0,0 +1,48 @@
"""
PRNG management routines, thin wrappers.
"""
import warnings
from OpenSSL._util import lib as _lib
warnings.warn(
"OpenSSL.rand is deprecated - you should use os.urandom instead",
DeprecationWarning,
stacklevel=3,
)
def add(buffer: bytes, entropy: int) -> None:
"""
Mix bytes from *string* into the PRNG state.
The *entropy* argument is (the lower bound of) an estimate of how much
randomness is contained in *string*, measured in bytes.
For more information, see e.g. :rfc:`1750`.
This function is only relevant if you are forking Python processes and
need to reseed the CSPRNG after fork.
:param buffer: Buffer with random data.
:param entropy: The entropy (in bytes) measurement of the buffer.
:return: :obj:`None`
"""
if not isinstance(buffer, bytes):
raise TypeError("buffer must be a byte string")
if not isinstance(entropy, int):
raise TypeError("entropy must be an integer")
_lib.RAND_add(buffer, len(buffer), entropy)
def status() -> int:
"""
Check whether the PRNG has been seeded with enough data.
:return: 1 if the PRNG is seeded enough, 0 otherwise.
"""
return _lib.RAND_status()

View File

@@ -0,0 +1,28 @@
# Copyright (C) AB Strakt
# Copyright (C) Jean-Paul Calderone
# See LICENSE for details.
"""
pyOpenSSL - A simple wrapper around the OpenSSL library
"""
__all__ = [
"__author__",
"__copyright__",
"__email__",
"__license__",
"__summary__",
"__title__",
"__uri__",
"__version__",
]
__version__ = "24.3.0"
__title__ = "pyOpenSSL"
__uri__ = "https://pyopenssl.org/"
__summary__ = "Python wrapper module around the OpenSSL library"
__author__ = "The pyOpenSSL developers"
__email__ = "cryptography-dev@python.org"
__license__ = "Apache License, Version 2.0"
__copyright__ = f"Copyright 2001-2024 {__author__}"

View File

@@ -259,21 +259,36 @@ class BlpImageFile(ImageFile.ImageFile):
def _open(self) -> None: def _open(self) -> None:
self.magic = self.fp.read(4) self.magic = self.fp.read(4)
if not _accept(self.magic):
self.fp.seek(5, os.SEEK_CUR)
(self._blp_alpha_depth,) = struct.unpack("<b", self.fp.read(1))
self.fp.seek(2, os.SEEK_CUR)
self._size = struct.unpack("<II", self.fp.read(8))
if self.magic in (b"BLP1", b"BLP2"):
decoder = self.magic.decode()
else:
msg = f"Bad BLP magic {repr(self.magic)}" msg = f"Bad BLP magic {repr(self.magic)}"
raise BLPFormatError(msg) raise BLPFormatError(msg)
self._mode = "RGBA" if self._blp_alpha_depth else "RGB" compression = struct.unpack("<i", self.fp.read(4))[0]
self.tile = [ImageFile._Tile(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))] if self.magic == b"BLP1":
alpha = struct.unpack("<I", self.fp.read(4))[0] != 0
else:
encoding = struct.unpack("<b", self.fp.read(1))[0]
alpha = struct.unpack("<b", self.fp.read(1))[0] != 0
alpha_encoding = struct.unpack("<b", self.fp.read(1))[0]
self.fp.seek(1, os.SEEK_CUR) # mips
self._size = struct.unpack("<II", self.fp.read(8))
args: tuple[int, int, bool] | tuple[int, int, bool, int]
if self.magic == b"BLP1":
encoding = struct.unpack("<i", self.fp.read(4))[0]
self.fp.seek(4, os.SEEK_CUR) # subtype
args = (compression, encoding, alpha)
offset = 28
else:
args = (compression, encoding, alpha, alpha_encoding)
offset = 20
decoder = self.magic.decode()
self._mode = "RGBA" if alpha else "RGB"
self.tile = [ImageFile._Tile(decoder, (0, 0) + self.size, offset, args)]
class _BLPBaseDecoder(ImageFile.PyDecoder): class _BLPBaseDecoder(ImageFile.PyDecoder):
@@ -281,7 +296,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]:
try: try:
self._read_blp_header() self._read_header()
self._load() self._load()
except struct.error as e: except struct.error as e:
msg = "Truncated BLP file" msg = "Truncated BLP file"
@@ -292,25 +307,9 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
def _load(self) -> None: def _load(self) -> None:
pass pass
def _read_blp_header(self) -> None: def _read_header(self) -> None:
assert self.fd is not None self._offsets = struct.unpack("<16I", self._safe_read(16 * 4))
self.fd.seek(4) self._lengths = struct.unpack("<16I", self._safe_read(16 * 4))
(self._blp_compression,) = struct.unpack("<i", self._safe_read(4))
(self._blp_encoding,) = struct.unpack("<b", self._safe_read(1))
(self._blp_alpha_depth,) = struct.unpack("<b", self._safe_read(1))
(self._blp_alpha_encoding,) = struct.unpack("<b", self._safe_read(1))
self.fd.seek(1, os.SEEK_CUR) # mips
self.size = struct.unpack("<II", self._safe_read(8))
if isinstance(self, BLP1Decoder):
# Only present for BLP1
(self._blp_encoding,) = struct.unpack("<i", self._safe_read(4))
self.fd.seek(4, os.SEEK_CUR) # subtype
self._blp_offsets = struct.unpack("<16I", self._safe_read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self._safe_read(16 * 4))
def _safe_read(self, length: int) -> bytes: def _safe_read(self, length: int) -> bytes:
assert self.fd is not None assert self.fd is not None
@@ -326,9 +325,11 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
ret.append((b, g, r, a)) ret.append((b, g, r, a))
return ret return ret
def _read_bgra(self, palette: list[tuple[int, int, int, int]]) -> bytearray: def _read_bgra(
self, palette: list[tuple[int, int, int, int]], alpha: bool
) -> bytearray:
data = bytearray() data = bytearray()
_data = BytesIO(self._safe_read(self._blp_lengths[0])) _data = BytesIO(self._safe_read(self._lengths[0]))
while True: while True:
try: try:
(offset,) = struct.unpack("<B", _data.read(1)) (offset,) = struct.unpack("<B", _data.read(1))
@@ -336,7 +337,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
break break
b, g, r, a = palette[offset] b, g, r, a = palette[offset]
d: tuple[int, ...] = (r, g, b) d: tuple[int, ...] = (r, g, b)
if self._blp_alpha_depth: if alpha:
d += (a,) d += (a,)
data.extend(d) data.extend(d)
return data return data
@@ -344,19 +345,21 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
class BLP1Decoder(_BLPBaseDecoder): class BLP1Decoder(_BLPBaseDecoder):
def _load(self) -> None: def _load(self) -> None:
if self._blp_compression == Format.JPEG: self._compression, self._encoding, alpha = self.args
if self._compression == Format.JPEG:
self._decode_jpeg_stream() self._decode_jpeg_stream()
elif self._blp_compression == 1: elif self._compression == 1:
if self._blp_encoding in (4, 5): if self._encoding in (4, 5):
palette = self._read_palette() palette = self._read_palette()
data = self._read_bgra(palette) data = self._read_bgra(palette, alpha)
self.set_as_raw(data) self.set_as_raw(data)
else: else:
msg = f"Unsupported BLP encoding {repr(self._blp_encoding)}" msg = f"Unsupported BLP encoding {repr(self._encoding)}"
raise BLPFormatError(msg) raise BLPFormatError(msg)
else: else:
msg = f"Unsupported BLP compression {repr(self._blp_encoding)}" msg = f"Unsupported BLP compression {repr(self._encoding)}"
raise BLPFormatError(msg) raise BLPFormatError(msg)
def _decode_jpeg_stream(self) -> None: def _decode_jpeg_stream(self) -> None:
@@ -365,8 +368,8 @@ class BLP1Decoder(_BLPBaseDecoder):
(jpeg_header_size,) = struct.unpack("<I", self._safe_read(4)) (jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
jpeg_header = self._safe_read(jpeg_header_size) jpeg_header = self._safe_read(jpeg_header_size)
assert self.fd is not None assert self.fd is not None
self._safe_read(self._blp_offsets[0] - self.fd.tell()) # What IS this? self._safe_read(self._offsets[0] - self.fd.tell()) # What IS this?
data = self._safe_read(self._blp_lengths[0]) data = self._safe_read(self._lengths[0])
data = jpeg_header + data data = jpeg_header + data
image = JpegImageFile(BytesIO(data)) image = JpegImageFile(BytesIO(data))
Image._decompression_bomb_check(image.size) Image._decompression_bomb_check(image.size)
@@ -383,47 +386,47 @@ class BLP1Decoder(_BLPBaseDecoder):
class BLP2Decoder(_BLPBaseDecoder): class BLP2Decoder(_BLPBaseDecoder):
def _load(self) -> None: def _load(self) -> None:
self._compression, self._encoding, alpha, self._alpha_encoding = self.args
palette = self._read_palette() palette = self._read_palette()
assert self.fd is not None assert self.fd is not None
self.fd.seek(self._blp_offsets[0]) self.fd.seek(self._offsets[0])
if self._blp_compression == 1: if self._compression == 1:
# Uncompressed or DirectX compression # Uncompressed or DirectX compression
if self._blp_encoding == Encoding.UNCOMPRESSED: if self._encoding == Encoding.UNCOMPRESSED:
data = self._read_bgra(palette) data = self._read_bgra(palette, alpha)
elif self._blp_encoding == Encoding.DXT: elif self._encoding == Encoding.DXT:
data = bytearray() data = bytearray()
if self._blp_alpha_encoding == AlphaEncoding.DXT1: if self._alpha_encoding == AlphaEncoding.DXT1:
linesize = (self.size[0] + 3) // 4 * 8 linesize = (self.state.xsize + 3) // 4 * 8
for yb in range((self.size[1] + 3) // 4): for yb in range((self.state.ysize + 3) // 4):
for d in decode_dxt1( for d in decode_dxt1(self._safe_read(linesize), alpha):
self._safe_read(linesize), alpha=bool(self._blp_alpha_depth)
):
data += d data += d
elif self._blp_alpha_encoding == AlphaEncoding.DXT3: elif self._alpha_encoding == AlphaEncoding.DXT3:
linesize = (self.size[0] + 3) // 4 * 16 linesize = (self.state.xsize + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4): for yb in range((self.state.ysize + 3) // 4):
for d in decode_dxt3(self._safe_read(linesize)): for d in decode_dxt3(self._safe_read(linesize)):
data += d data += d
elif self._blp_alpha_encoding == AlphaEncoding.DXT5: elif self._alpha_encoding == AlphaEncoding.DXT5:
linesize = (self.size[0] + 3) // 4 * 16 linesize = (self.state.xsize + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4): for yb in range((self.state.ysize + 3) // 4):
for d in decode_dxt5(self._safe_read(linesize)): for d in decode_dxt5(self._safe_read(linesize)):
data += d data += d
else: else:
msg = f"Unsupported alpha encoding {repr(self._blp_alpha_encoding)}" msg = f"Unsupported alpha encoding {repr(self._alpha_encoding)}"
raise BLPFormatError(msg) raise BLPFormatError(msg)
else: else:
msg = f"Unknown BLP encoding {repr(self._blp_encoding)}" msg = f"Unknown BLP encoding {repr(self._encoding)}"
raise BLPFormatError(msg) raise BLPFormatError(msg)
else: else:
msg = f"Unknown BLP compression {repr(self._blp_compression)}" msg = f"Unknown BLP compression {repr(self._compression)}"
raise BLPFormatError(msg) raise BLPFormatError(msg)
self.set_as_raw(data) self.set_as_raw(data)
@@ -472,10 +475,15 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
assert im.palette is not None assert im.palette is not None
fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
fp.write(struct.pack("<b", 1 if im.palette.mode == "RGBA" else 0)) alpha_depth = 1 if im.palette.mode == "RGBA" else 0
fp.write(struct.pack("<b", 0)) # alpha encoding if magic == b"BLP1":
fp.write(struct.pack("<b", 0)) # mips fp.write(struct.pack("<L", alpha_depth))
else:
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
fp.write(struct.pack("<b", alpha_depth))
fp.write(struct.pack("<b", 0)) # alpha encoding
fp.write(struct.pack("<b", 0)) # mips
fp.write(struct.pack("<II", *im.size)) fp.write(struct.pack("<II", *im.size))
if magic == b"BLP1": if magic == b"BLP1":
fp.write(struct.pack("<i", 5)) fp.write(struct.pack("<i", 5))

View File

@@ -560,9 +560,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
+ struct.pack("<4I", *rgba_mask) # dwRGBABitMask + struct.pack("<4I", *rgba_mask) # dwRGBABitMask
+ struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0) + struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0)
) )
ImageFile._save( ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)])
im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]
)
def _accept(prefix: bytes) -> bool: def _accept(prefix: bytes) -> bool:

View File

@@ -454,7 +454,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -
if hasattr(fp, "flush"): if hasattr(fp, "flush"):
fp.flush() fp.flush()
ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size, 0, None)]) ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size)])
fp.write(b"\n%%%%EndBinary\n") fp.write(b"\n%%%%EndBinary\n")
fp.write(b"grestore end\n") fp.write(b"grestore end\n")

View File

@@ -303,38 +303,38 @@ TAGS = {
class GPS(IntEnum): class GPS(IntEnum):
GPSVersionID = 0 GPSVersionID = 0x00
GPSLatitudeRef = 1 GPSLatitudeRef = 0x01
GPSLatitude = 2 GPSLatitude = 0x02
GPSLongitudeRef = 3 GPSLongitudeRef = 0x03
GPSLongitude = 4 GPSLongitude = 0x04
GPSAltitudeRef = 5 GPSAltitudeRef = 0x05
GPSAltitude = 6 GPSAltitude = 0x06
GPSTimeStamp = 7 GPSTimeStamp = 0x07
GPSSatellites = 8 GPSSatellites = 0x08
GPSStatus = 9 GPSStatus = 0x09
GPSMeasureMode = 10 GPSMeasureMode = 0x0A
GPSDOP = 11 GPSDOP = 0x0B
GPSSpeedRef = 12 GPSSpeedRef = 0x0C
GPSSpeed = 13 GPSSpeed = 0x0D
GPSTrackRef = 14 GPSTrackRef = 0x0E
GPSTrack = 15 GPSTrack = 0x0F
GPSImgDirectionRef = 16 GPSImgDirectionRef = 0x10
GPSImgDirection = 17 GPSImgDirection = 0x11
GPSMapDatum = 18 GPSMapDatum = 0x12
GPSDestLatitudeRef = 19 GPSDestLatitudeRef = 0x13
GPSDestLatitude = 20 GPSDestLatitude = 0x14
GPSDestLongitudeRef = 21 GPSDestLongitudeRef = 0x15
GPSDestLongitude = 22 GPSDestLongitude = 0x16
GPSDestBearingRef = 23 GPSDestBearingRef = 0x17
GPSDestBearing = 24 GPSDestBearing = 0x18
GPSDestDistanceRef = 25 GPSDestDistanceRef = 0x19
GPSDestDistance = 26 GPSDestDistance = 0x1A
GPSProcessingMethod = 27 GPSProcessingMethod = 0x1B
GPSAreaInformation = 28 GPSAreaInformation = 0x1C
GPSDateStamp = 29 GPSDateStamp = 0x1D
GPSDifferential = 30 GPSDifferential = 0x1E
GPSHPositioningError = 31 GPSHPositioningError = 0x1F
"""Maps EXIF GPS tags to tag names.""" """Maps EXIF GPS tags to tag names."""
@@ -342,40 +342,41 @@ GPSTAGS = {i.value: i.name for i in GPS}
class Interop(IntEnum): class Interop(IntEnum):
InteropIndex = 1 InteropIndex = 0x0001
InteropVersion = 2 InteropVersion = 0x0002
RelatedImageFileFormat = 4096 RelatedImageFileFormat = 0x1000
RelatedImageWidth = 4097 RelatedImageWidth = 0x1001
RelatedImageHeight = 4098 RelatedImageHeight = 0x1002
class IFD(IntEnum): class IFD(IntEnum):
Exif = 34665 Exif = 0x8769
GPSInfo = 34853 GPSInfo = 0x8825
Makernote = 37500 MakerNote = 0x927C
Interop = 40965 Makernote = 0x927C # Deprecated
Interop = 0xA005
IFD1 = -1 IFD1 = -1
class LightSource(IntEnum): class LightSource(IntEnum):
Unknown = 0 Unknown = 0x00
Daylight = 1 Daylight = 0x01
Fluorescent = 2 Fluorescent = 0x02
Tungsten = 3 Tungsten = 0x03
Flash = 4 Flash = 0x04
Fine = 9 Fine = 0x09
Cloudy = 10 Cloudy = 0x0A
Shade = 11 Shade = 0x0B
DaylightFluorescent = 12 DaylightFluorescent = 0x0C
DayWhiteFluorescent = 13 DayWhiteFluorescent = 0x0D
CoolWhiteFluorescent = 14 CoolWhiteFluorescent = 0x0E
WhiteFluorescent = 15 WhiteFluorescent = 0x0F
StandardLightA = 17 StandardLightA = 0x11
StandardLightB = 18 StandardLightB = 0x12
StandardLightC = 19 StandardLightC = 0x13
D55 = 20 D55 = 0x14
D65 = 21 D65 = 0x15
D75 = 22 D75 = 0x16
D50 = 23 D50 = 0x17
ISO = 24 ISO = 0x18
Other = 255 Other = 0xFF

View File

@@ -159,7 +159,7 @@ class FliImageFile(ImageFile.ImageFile):
framesize = i32(s) framesize = i32(s)
self.decodermaxblock = framesize self.decodermaxblock = framesize
self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset, None)] self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset)]
self.__offset += framesize self.__offset += framesize

View File

@@ -170,7 +170,7 @@ class FpxImageFile(ImageFile.ImageFile):
"raw", "raw",
(x, y, x1, y1), (x, y, x1, y1),
i32(s, i) + 28, i32(s, i) + 28,
(self.rawmode,), self.rawmode,
) )
) )

View File

@@ -95,7 +95,7 @@ class FtexImageFile(ImageFile.ImageFile):
self._mode = "RGBA" self._mode = "RGBA"
self.tile = [ImageFile._Tile("bcn", (0, 0) + self.size, 0, (1,))] self.tile = [ImageFile._Tile("bcn", (0, 0) + self.size, 0, (1,))]
elif format == Format.UNCOMPRESSED: elif format == Format.UNCOMPRESSED:
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))] self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, "RGB")]
else: else:
msg = f"Invalid texture compression format: {repr(format)}" msg = f"Invalid texture compression format: {repr(format)}"
raise ValueError(msg) raise ValueError(msg)

View File

@@ -76,7 +76,7 @@ class GdImageFile(ImageFile.ImageFile):
"raw", "raw",
(0, 0) + self.size, (0, 0) + self.size,
7 + true_color_offset + 4 + 256 * 4, 7 + true_color_offset + 4 + 256 * 4,
("L", 0, 1), "L",
) )
] ]

View File

@@ -103,7 +103,6 @@ class GifImageFile(ImageFile.ImageFile):
self.info["version"] = s[:6] self.info["version"] = s[:6]
self._size = i16(s, 6), i16(s, 8) self._size = i16(s, 6), i16(s, 8)
self.tile = []
flags = s[10] flags = s[10]
bits = (flags & 7) + 1 bits = (flags & 7) + 1
@@ -696,8 +695,9 @@ def _write_multiple_frames(
) )
background = _get_background(im_frame, color) background = _get_background(im_frame, color)
background_im = Image.new("P", im_frame.size, background) background_im = Image.new("P", im_frame.size, background)
assert im_frames[0].im.palette is not None first_palette = im_frames[0].im.palette
background_im.putpalette(im_frames[0].im.palette) assert first_palette is not None
background_im.putpalette(first_palette, first_palette.mode)
bbox = _getbbox(background_im, im_frame)[1] bbox = _getbbox(background_im, im_frame)[1]
elif encoderinfo.get("optimize") and im_frame.mode != "1": elif encoderinfo.get("optimize") and im_frame.mode != "1":
if "transparency" not in encoderinfo: if "transparency" not in encoderinfo:

View File

@@ -357,7 +357,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
name = "".join([name[: 92 - len(ext)], ext]) name = "".join([name[: 92 - len(ext)], ext])
fp.write(f"Name: {name}\r\n".encode("ascii")) fp.write(f"Name: {name}\r\n".encode("ascii"))
fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode("ascii")) fp.write(f"Image size (x*y): {im.size[0]}*{im.size[1]}\r\n".encode("ascii"))
fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) fp.write(f"File size (no of images): {frames}\r\n".encode("ascii"))
if im.mode in ["P", "PA"]: if im.mode in ["P", "PA"]:
fp.write(b"Lut: 1\r\n") fp.write(b"Lut: 1\r\n")

View File

@@ -692,13 +692,10 @@ class Image:
) )
def __repr__(self) -> str: def __repr__(self) -> str:
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( return (
self.__class__.__module__, f"<{self.__class__.__module__}.{self.__class__.__name__} "
self.__class__.__name__, f"image mode={self.mode} size={self.size[0]}x{self.size[1]} "
self.mode, f"at 0x{id(self):X}>"
self.size[0],
self.size[1],
id(self),
) )
def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None: def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None:
@@ -707,14 +704,8 @@ class Image:
# Same as __repr__ but without unpredictable id(self), # Same as __repr__ but without unpredictable id(self),
# to keep Jupyter notebook `text/plain` output stable. # to keep Jupyter notebook `text/plain` output stable.
p.text( p.text(
"<%s.%s image mode=%s size=%dx%d>" f"<{self.__class__.__module__}.{self.__class__.__name__} "
% ( f"image mode={self.mode} size={self.size[0]}x{self.size[1]}>"
self.__class__.__module__,
self.__class__.__name__,
self.mode,
self.size[0],
self.size[1],
)
) )
def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None: def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None:
@@ -763,7 +754,7 @@ class Image:
def __setstate__(self, state: list[Any]) -> None: def __setstate__(self, state: list[Any]) -> None:
Image.__init__(self) Image.__init__(self)
info, mode, size, palette, data = state info, mode, size, palette, data = state[:5]
self.info = info self.info = info
self._mode = mode self._mode = mode
self._size = size self._size = size
@@ -1574,7 +1565,7 @@ class Image:
for subifd_offset in subifd_offsets: for subifd_offset in subifd_offsets:
ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset))
ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) ifd1 = exif.get_ifd(ExifTags.IFD.IFD1)
if ifd1 and ifd1.get(513): if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset):
assert exif._info is not None assert exif._info is not None
ifds.append((ifd1, exif._info.next)) ifds.append((ifd1, exif._info.next))
@@ -1586,11 +1577,11 @@ class Image:
fp = self.fp fp = self.fp
if ifd is not None: if ifd is not None:
thumbnail_offset = ifd.get(513) thumbnail_offset = ifd.get(ExifTags.Base.JpegIFOffset)
if thumbnail_offset is not None: if thumbnail_offset is not None:
thumbnail_offset += getattr(self, "_exif_offset", 0) thumbnail_offset += getattr(self, "_exif_offset", 0)
self.fp.seek(thumbnail_offset) self.fp.seek(thumbnail_offset)
data = self.fp.read(ifd.get(514)) data = self.fp.read(ifd.get(ExifTags.Base.JpegIFByteCount))
fp = io.BytesIO(data) fp = io.BytesIO(data)
with open(fp) as im: with open(fp) as im:
@@ -2550,7 +2541,7 @@ class Image:
filename: str | bytes = "" filename: str | bytes = ""
open_fp = False open_fp = False
if is_path(fp): if is_path(fp):
filename = os.path.realpath(os.fspath(fp)) filename = os.fspath(fp)
open_fp = True open_fp = True
elif fp == sys.stdout: elif fp == sys.stdout:
try: try:
@@ -2559,13 +2550,13 @@ class Image:
pass pass
if not filename and hasattr(fp, "name") and is_path(fp.name): if not filename and hasattr(fp, "name") and is_path(fp.name):
# only set the name for metadata purposes # only set the name for metadata purposes
filename = os.path.realpath(os.fspath(fp.name)) filename = os.fspath(fp.name)
# may mutate self! # may mutate self!
self._ensure_mutable() self._ensure_mutable()
save_all = params.pop("save_all", False) save_all = params.pop("save_all", False)
self.encoderinfo = params self.encoderinfo = {**getattr(self, "encoderinfo", {}), **params}
self.encoderconfig: tuple[Any, ...] = () self.encoderconfig: tuple[Any, ...] = ()
preinit() preinit()
@@ -2612,6 +2603,11 @@ class Image:
except PermissionError: except PermissionError:
pass pass
raise raise
finally:
try:
del self.encoderinfo
except AttributeError:
pass
if open_fp: if open_fp:
fp.close() fp.close()
@@ -3463,7 +3459,7 @@ def open(
exclusive_fp = False exclusive_fp = False
filename: str | bytes = "" filename: str | bytes = ""
if is_path(fp): if is_path(fp):
filename = os.path.realpath(os.fspath(fp)) filename = os.fspath(fp)
if filename: if filename:
fp = builtins.open(filename, "rb") fp = builtins.open(filename, "rb")
@@ -3893,7 +3889,7 @@ class Exif(_ExifBase):
gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo)
print(gps_ifd) print(gps_ifd)
Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.Makernote``, Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.MakerNote``,
``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``. ``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``.
:py:mod:`~PIL.ExifTags` also has enum classes to provide names for data:: :py:mod:`~PIL.ExifTags` also has enum classes to provide names for data::
@@ -4027,6 +4023,9 @@ class Exif(_ExifBase):
head = self._get_head() head = self._get_head()
ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
for tag, ifd_dict in self._ifds.items():
if tag not in self:
ifd[tag] = ifd_dict
for tag, value in self.items(): for tag, value in self.items():
if tag in [ if tag in [
ExifTags.IFD.Exif, ExifTags.IFD.Exif,
@@ -4056,11 +4055,11 @@ class Exif(_ExifBase):
ifd = self._get_ifd_dict(offset, tag) ifd = self._get_ifd_dict(offset, tag)
if ifd is not None: if ifd is not None:
self._ifds[tag] = ifd self._ifds[tag] = ifd
elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.Makernote]: elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.MakerNote]:
if ExifTags.IFD.Exif not in self._ifds: if ExifTags.IFD.Exif not in self._ifds:
self.get_ifd(ExifTags.IFD.Exif) self.get_ifd(ExifTags.IFD.Exif)
tag_data = self._ifds[ExifTags.IFD.Exif][tag] tag_data = self._ifds[ExifTags.IFD.Exif][tag]
if tag == ExifTags.IFD.Makernote: if tag == ExifTags.IFD.MakerNote:
from .TiffImagePlugin import ImageFileDirectory_v2 from .TiffImagePlugin import ImageFileDirectory_v2
if tag_data[:8] == b"FUJIFILM": if tag_data[:8] == b"FUJIFILM":
@@ -4147,7 +4146,7 @@ class Exif(_ExifBase):
ifd = { ifd = {
k: v k: v
for (k, v) in ifd.items() for (k, v) in ifd.items()
if k not in (ExifTags.IFD.Interop, ExifTags.IFD.Makernote) if k not in (ExifTags.IFD.Interop, ExifTags.IFD.MakerNote)
} }
return ifd return ifd

View File

@@ -98,8 +98,8 @@ def _tilesort(t: _Tile) -> int:
class _Tile(NamedTuple): class _Tile(NamedTuple):
codec_name: str codec_name: str
extents: tuple[int, int, int, int] | None extents: tuple[int, int, int, int] | None
offset: int offset: int = 0
args: tuple[Any, ...] | str | None args: tuple[Any, ...] | str | None = None
# #
@@ -120,7 +120,7 @@ class ImageFile(Image.Image):
self.custom_mimetype: str | None = None self.custom_mimetype: str | None = None
self.tile: list[_Tile] = [] self.tile: list[_Tile] = []
""" A list of tile descriptors, or ``None`` """ """ A list of tile descriptors """
self.readonly = 1 # until we know better self.readonly = 1 # until we know better
@@ -130,7 +130,7 @@ class ImageFile(Image.Image):
if is_path(fp): if is_path(fp):
# filename # filename
self.fp = open(fp, "rb") self.fp = open(fp, "rb")
self.filename = os.path.realpath(os.fspath(fp)) self.filename = os.fspath(fp)
self._exclusive_fp = True self._exclusive_fp = True
else: else:
# stream # stream

View File

@@ -553,7 +553,7 @@ class Color3DLUT(MultibandFilter):
ch_out = channels or ch_in ch_out = channels or ch_in
size_1d, size_2d, size_3d = self.size size_1d, size_2d, size_3d = self.size
table = [0] * (size_1d * size_2d * size_3d * ch_out) table: list[float] = [0] * (size_1d * size_2d * size_3d * ch_out)
idx_in = 0 idx_in = 0
idx_out = 0 idx_out = 0
for b in range(size_3d): for b in range(size_3d):

View File

@@ -270,7 +270,7 @@ class FreeTypeFont:
) )
if is_path(font): if is_path(font):
font = os.path.realpath(os.fspath(font)) font = os.fspath(font)
if sys.platform == "win32": if sys.platform == "win32":
font_bytes_path = font if isinstance(font, bytes) else font.encode() font_bytes_path = font if isinstance(font, bytes) else font.encode()
try: try:

View File

@@ -104,28 +104,17 @@ def grab(
def grabclipboard() -> Image.Image | list[str] | None: def grabclipboard() -> Image.Image | list[str] | None:
if sys.platform == "darwin": if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp(".png") p = subprocess.run(
os.close(fh) ["osascript", "-e", "get the clipboard as «class PNGf»"],
commands = [ capture_output=True,
'set theFile to (open for access POSIX file "' )
+ filepath if p.returncode != 0:
+ '" with write permission)', return None
"try",
" write (the clipboard as «class PNGf») to theFile",
"end try",
"close access theFile",
]
script = ["osascript"]
for command in commands:
script += ["-e", command]
subprocess.call(script)
im = None import binascii
if os.stat(filepath).st_size != 0:
im = Image.open(filepath) data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3]))
im.load() return Image.open(data)
os.unlink(filepath)
return im
elif sys.platform == "win32": elif sys.platform == "win32":
fmt, data = Image.core.grabclipboard_win32() fmt, data = Image.core.grabclipboard_win32()
if fmt == "file": # CF_HDROP if fmt == "file": # CF_HDROP

View File

@@ -698,10 +698,11 @@ def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image
8: Image.Transpose.ROTATE_90, 8: Image.Transpose.ROTATE_90,
}.get(orientation) }.get(orientation)
if method is not None: if method is not None:
transposed_image = image.transpose(method)
if in_place: if in_place:
image.im = transposed_image.im image.im = image.im.transpose(method)
image._size = transposed_image._size image._size = image.im.size
else:
transposed_image = image.transpose(method)
exif_image = image if in_place else transposed_image exif_image = image if in_place else transposed_image
exif = exif_image.getexif() exif = exif_image.getexif()

View File

@@ -213,4 +213,7 @@ def toqimage(im: Image.Image | str | QByteArray) -> ImageQt:
def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap: def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap:
qimage = toqimage(im) qimage = toqimage(im)
return getattr(QPixmap, "fromImage")(qimage) pixmap = getattr(QPixmap, "fromImage")(qimage)
if qt_version == "6":
pixmap.detach()
return pixmap

View File

@@ -62,7 +62,7 @@ class ImtImageFile(ImageFile.ImageFile):
"raw", "raw",
(0, 0) + self.size, (0, 0) + self.size,
self.fp.tell() - len(buffer), self.fp.tell() - len(buffer),
(self.mode, 0, 1), self.mode,
) )
] ]

View File

@@ -252,6 +252,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
if sig == b"\xff\x4f\xff\x51": if sig == b"\xff\x4f\xff\x51":
self.codec = "j2k" self.codec = "j2k"
self._size, self._mode = _parse_codestream(self.fp) self._size, self._mode = _parse_codestream(self.fp)
self._parse_comment()
else: else:
sig = sig + self.fp.read(8) sig = sig + self.fp.read(8)
@@ -262,6 +263,9 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
if dpi is not None: if dpi is not None:
self.info["dpi"] = dpi self.info["dpi"] = dpi
if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"): if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"):
hdr = self.fp.read(2)
length = _binary.i16be(hdr)
self.fp.seek(length - 2, os.SEEK_CUR)
self._parse_comment() self._parse_comment()
else: else:
msg = "not a JPEG 2000 file" msg = "not a JPEG 2000 file"
@@ -296,10 +300,6 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
] ]
def _parse_comment(self) -> None: def _parse_comment(self) -> None:
hdr = self.fp.read(2)
length = _binary.i16be(hdr)
self.fp.seek(length - 2, os.SEEK_CUR)
while True: while True:
marker = self.fp.read(2) marker = self.fp.read(2)
if not marker: if not marker:

View File

@@ -72,7 +72,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
n = i16(self.fp.read(2)) - 2 n = i16(self.fp.read(2)) - 2
s = ImageFile._safe_read(self.fp, n) s = ImageFile._safe_read(self.fp, n)
app = "APP%d" % (marker & 15) app = f"APP{marker & 15}"
self.app[app] = s # compatibility self.app[app] = s # compatibility
self.applist.append((app, s)) self.applist.append((app, s))
@@ -90,6 +90,9 @@ def APP(self: JpegImageFile, marker: int) -> None:
else: else:
if jfif_unit == 1: if jfif_unit == 1:
self.info["dpi"] = jfif_density self.info["dpi"] = jfif_density
elif jfif_unit == 2: # cm
# 1 dpcm = 2.54 dpi
self.info["dpi"] = tuple(d * 2.54 for d in jfif_density)
self.info["jfif_unit"] = jfif_unit self.info["jfif_unit"] = jfif_unit
self.info["jfif_density"] = jfif_density self.info["jfif_density"] = jfif_density
elif marker == 0xFFE1 and s[:6] == b"Exif\0\0": elif marker == 0xFFE1 and s[:6] == b"Exif\0\0":
@@ -395,6 +398,13 @@ class JpegImageFile(ImageFile.ImageFile):
return getattr(self, "_" + name) return getattr(self, "_" + name)
raise AttributeError(name) raise AttributeError(name)
def __getstate__(self) -> list[Any]:
return super().__getstate__() + [self.layers, self.layer]
def __setstate__(self, state: list[Any]) -> None:
super().__setstate__(state)
self.layers, self.layer = state[5:]
def load_read(self, read_bytes: int) -> bytes: def load_read(self, read_bytes: int) -> bytes:
""" """
internal: read more image data internal: read more image data
@@ -751,7 +761,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
extra = info.get("extra", b"") extra = info.get("extra", b"")
MAX_BYTES_IN_MARKER = 65533 MAX_BYTES_IN_MARKER = 65533
xmp = info.get("xmp", im.info.get("xmp")) xmp = info.get("xmp")
if xmp: if xmp:
overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00" overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00"
max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len

View File

@@ -70,9 +70,9 @@ class MspImageFile(ImageFile.ImageFile):
self._size = i16(s, 4), i16(s, 6) self._size = i16(s, 4), i16(s, 6)
if s[:4] == b"DanM": if s[:4] == b"DanM":
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, ("1", 0, 1))] self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")]
else: else:
self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32, None)] self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)]
class MspDecoder(ImageFile.PyDecoder): class MspDecoder(ImageFile.PyDecoder):
@@ -188,7 +188,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
fp.write(o16(h)) fp.write(o16(h))
# image body # image body
ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, ("1", 0, 1))]) ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, "1")])
# #

View File

@@ -47,7 +47,7 @@ class PcdImageFile(ImageFile.ImageFile):
self._mode = "RGB" self._mode = "RGB"
self._size = 768, 512 # FIXME: not correct for rotated images! self._size = 768, 512 # FIXME: not correct for rotated images!
self.tile = [ImageFile._Tile("pcd", (0, 0) + self.size, 96 * 2048, None)] self.tile = [ImageFile._Tile("pcd", (0, 0) + self.size, 96 * 2048)]
def load_end(self) -> None: def load_end(self) -> None:
if self.tile_post_rotate: if self.tile_post_rotate:

View File

@@ -86,7 +86,7 @@ class PcxImageFile(ImageFile.ImageFile):
elif bits == 1 and planes in (2, 4): elif bits == 1 and planes in (2, 4):
mode = "P" mode = "P"
rawmode = "P;%dL" % planes rawmode = f"P;{planes}L"
self.palette = ImagePalette.raw("RGB", s[16:64]) self.palette = ImagePalette.raw("RGB", s[16:64])
elif version == 5 and bits == 8 and planes == 1: elif version == 5 and bits == 8 and planes == 1:

View File

@@ -61,9 +61,7 @@ class PixarImageFile(ImageFile.ImageFile):
# FIXME: to be continued... # FIXME: to be continued...
# create tile descriptor (assuming "dumped") # create tile descriptor (assuming "dumped")
self.tile = [ self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 1024, self.mode)]
ImageFile._Tile("raw", (0, 0) + self.size, 1024, (self.mode, 0, 1))
]
# #

View File

@@ -523,7 +523,7 @@ class PngStream(ChunkStream):
assert self.fp is not None assert self.fp is not None
s = ImageFile._safe_read(self.fp, length) s = ImageFile._safe_read(self.fp, length)
raw_vals = struct.unpack(">%dI" % (len(s) // 4), s) raw_vals = struct.unpack(f">{len(s) // 4}I", s)
self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals)
return s return s

View File

@@ -32,7 +32,7 @@ class QoiImageFile(ImageFile.ImageFile):
self._mode = "RGB" if channels == 3 else "RGBA" self._mode = "RGB" if channels == 3 else "RGBA"
self.fp.seek(1, os.SEEK_CUR) # colorspace self.fp.seek(1, os.SEEK_CUR) # colorspace
self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell(), None)] self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell())]
class QoiDecoder(ImageFile.PyDecoder): class QoiDecoder(ImageFile.PyDecoder):

View File

@@ -154,9 +154,7 @@ class SpiderImageFile(ImageFile.ImageFile):
self.rawmode = "F;32F" self.rawmode = "F;32F"
self._mode = "F" self._mode = "F"
self.tile = [ self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, offset, self.rawmode)]
ImageFile._Tile("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))
]
self._fp = self.fp # FIXME: hack self._fp = self.fp # FIXME: hack
@property @property
@@ -211,26 +209,27 @@ class SpiderImageFile(ImageFile.ImageFile):
# given a list of filenames, return a list of images # given a list of filenames, return a list of images
def loadImageSeries(filelist: list[str] | None = None) -> list[SpiderImageFile] | None: def loadImageSeries(filelist: list[str] | None = None) -> list[Image.Image] | None:
"""create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
if filelist is None or len(filelist) < 1: if filelist is None or len(filelist) < 1:
return None return None
imglist = [] byte_imgs = []
for img in filelist: for img in filelist:
if not os.path.exists(img): if not os.path.exists(img):
print(f"unable to find {img}") print(f"unable to find {img}")
continue continue
try: try:
with Image.open(img) as im: with Image.open(img) as im:
im = im.convert2byte() assert isinstance(im, SpiderImageFile)
byte_im = im.convert2byte()
except Exception: except Exception:
if not isSpiderImage(img): if not isSpiderImage(img):
print(f"{img} is not a Spider image file") print(f"{img} is not a Spider image file")
continue continue
im.info["filename"] = img byte_im.info["filename"] = img
imglist.append(im) byte_imgs.append(byte_im)
return imglist return byte_imgs
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@@ -280,9 +279,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
fp.writelines(hdr) fp.writelines(hdr)
rawmode = "F;32NF" # 32-bit native floating point rawmode = "F;32NF" # 32-bit native floating point
ImageFile._save( ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)])
im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]
)
def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:

View File

@@ -294,7 +294,7 @@ def _accept(prefix: bytes) -> bool:
def _limit_rational( def _limit_rational(
val: float | Fraction | IFDRational, max_val: int val: float | Fraction | IFDRational, max_val: int
) -> tuple[IntegralLike, IntegralLike]: ) -> tuple[IntegralLike, IntegralLike]:
inv = abs(float(val)) > 1 inv = abs(val) > 1
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
return n_d[::-1] if inv else n_d return n_d[::-1] if inv else n_d
@@ -582,7 +582,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
def __init__( def __init__(
self, self,
ifh: bytes = b"II\052\0\0\0\0\0", ifh: bytes = b"II\x2A\x00\x00\x00\x00\x00",
prefix: bytes | None = None, prefix: bytes | None = None,
group: int | None = None, group: int | None = None,
) -> None: ) -> None:
@@ -685,22 +685,33 @@ class ImageFileDirectory_v2(_IFDv2Base):
else: else:
self.tagtype[tag] = TiffTags.UNDEFINED self.tagtype[tag] = TiffTags.UNDEFINED
if all(isinstance(v, IFDRational) for v in values): if all(isinstance(v, IFDRational) for v in values):
self.tagtype[tag] = ( for v in values:
TiffTags.RATIONAL assert isinstance(v, IFDRational)
if all(v >= 0 for v in values) if v < 0:
else TiffTags.SIGNED_RATIONAL self.tagtype[tag] = TiffTags.SIGNED_RATIONAL
) break
elif all(isinstance(v, int) for v in values):
if all(0 <= v < 2**16 for v in values):
self.tagtype[tag] = TiffTags.SHORT
elif all(-(2**15) < v < 2**15 for v in values):
self.tagtype[tag] = TiffTags.SIGNED_SHORT
else: else:
self.tagtype[tag] = ( self.tagtype[tag] = TiffTags.RATIONAL
TiffTags.LONG elif all(isinstance(v, int) for v in values):
if all(v >= 0 for v in values) short = True
else TiffTags.SIGNED_LONG signed_short = True
) long = True
for v in values:
assert isinstance(v, int)
if short and not (0 <= v < 2**16):
short = False
if signed_short and not (-(2**15) < v < 2**15):
signed_short = False
if long and v < 0:
long = False
if short:
self.tagtype[tag] = TiffTags.SHORT
elif signed_short:
self.tagtype[tag] = TiffTags.SIGNED_SHORT
elif long:
self.tagtype[tag] = TiffTags.LONG
else:
self.tagtype[tag] = TiffTags.SIGNED_LONG
elif all(isinstance(v, float) for v in values): elif all(isinstance(v, float) for v in values):
self.tagtype[tag] = TiffTags.DOUBLE self.tagtype[tag] = TiffTags.DOUBLE
elif all(isinstance(v, str) for v in values): elif all(isinstance(v, str) for v in values):
@@ -718,7 +729,10 @@ class ImageFileDirectory_v2(_IFDv2Base):
is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict) is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict)
if not is_ifd: if not is_ifd:
values = tuple(info.cvt_enum(value) for value in values) values = tuple(
info.cvt_enum(value) if isinstance(value, str) else value
for value in values
)
dest = self._tags_v1 if legacy_api else self._tags_v2 dest = self._tags_v1 if legacy_api else self._tags_v2
@@ -921,9 +935,9 @@ class ImageFileDirectory_v2(_IFDv2Base):
self._tagdata[tag] = data self._tagdata[tag] = data
self.tagtype[tag] = typ self.tagtype[tag] = typ
msg += " - value: " + ( msg += " - value: "
"<table: %d bytes>" % size if size > 32 else repr(data) msg += f"<table: {size} bytes>" if size > 32 else repr(data)
)
logger.debug(msg) logger.debug(msg)
(self.next,) = ( (self.next,) = (
@@ -935,16 +949,26 @@ class ImageFileDirectory_v2(_IFDv2Base):
warnings.warn(str(msg)) warnings.warn(str(msg))
return return
def _get_ifh(self):
ifh = self._prefix + self._pack("H", 43 if self._bigtiff else 42)
if self._bigtiff:
ifh += self._pack("HH", 8, 0)
ifh += self._pack("Q", 16) if self._bigtiff else self._pack("L", 8)
return ifh
def tobytes(self, offset: int = 0) -> bytes: def tobytes(self, offset: int = 0) -> bytes:
# FIXME What about tagdata? # FIXME What about tagdata?
result = self._pack("H", len(self._tags_v2)) result = self._pack("Q" if self._bigtiff else "H", len(self._tags_v2))
entries: list[tuple[int, int, int, bytes, bytes]] = [] entries: list[tuple[int, int, int, bytes, bytes]] = []
offset = offset + len(result) + len(self._tags_v2) * 12 + 4 offset += len(result) + len(self._tags_v2) * (20 if self._bigtiff else 12) + 4
stripoffsets = None stripoffsets = None
# pass 1: convert tags to binary format # pass 1: convert tags to binary format
# always write tags in ascending order # always write tags in ascending order
fmt = "Q" if self._bigtiff else "L"
fmt_size = 8 if self._bigtiff else 4
for tag, value in sorted(self._tags_v2.items()): for tag, value in sorted(self._tags_v2.items()):
if tag == STRIPOFFSETS: if tag == STRIPOFFSETS:
stripoffsets = len(entries) stripoffsets = len(entries)
@@ -952,11 +976,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value)) logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value))
is_ifd = typ == TiffTags.LONG and isinstance(value, dict) is_ifd = typ == TiffTags.LONG and isinstance(value, dict)
if is_ifd: if is_ifd:
if self._endian == "<": ifd = ImageFileDirectory_v2(self._get_ifh(), group=tag)
ifh = b"II\x2A\x00\x08\x00\x00\x00"
else:
ifh = b"MM\x00\x2A\x00\x00\x00\x08"
ifd = ImageFileDirectory_v2(ifh, group=tag)
values = self._tags_v2[tag] values = self._tags_v2[tag]
for ifd_tag, ifd_value in values.items(): for ifd_tag, ifd_value in values.items():
ifd[ifd_tag] = ifd_value ifd[ifd_tag] = ifd_value
@@ -967,10 +987,8 @@ class ImageFileDirectory_v2(_IFDv2Base):
tagname = TiffTags.lookup(tag, self.group).name tagname = TiffTags.lookup(tag, self.group).name
typname = "ifd" if is_ifd else TYPES.get(typ, "unknown") typname = "ifd" if is_ifd else TYPES.get(typ, "unknown")
msg = f"save: {tagname} ({tag}) - type: {typname} ({typ})" msg = f"save: {tagname} ({tag}) - type: {typname} ({typ}) - value: "
msg += " - value: " + ( msg += f"<table: {len(data)} bytes>" if len(data) >= 16 else str(values)
"<table: %d bytes>" % len(data) if len(data) >= 16 else str(values)
)
logger.debug(msg) logger.debug(msg)
# count is sum of lengths for string and arbitrary data # count is sum of lengths for string and arbitrary data
@@ -981,10 +999,10 @@ class ImageFileDirectory_v2(_IFDv2Base):
else: else:
count = len(values) count = len(values)
# figure out if data fits into the entry # figure out if data fits into the entry
if len(data) <= 4: if len(data) <= fmt_size:
entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) entries.append((tag, typ, count, data.ljust(fmt_size, b"\0"), b""))
else: else:
entries.append((tag, typ, count, self._pack("L", offset), data)) entries.append((tag, typ, count, self._pack(fmt, offset), data))
offset += (len(data) + 1) // 2 * 2 # pad to word offset += (len(data) + 1) // 2 * 2 # pad to word
# update strip offset data to point beyond auxiliary data # update strip offset data to point beyond auxiliary data
@@ -995,13 +1013,15 @@ class ImageFileDirectory_v2(_IFDv2Base):
values = [val + offset for val in handler(self, data, self.legacy_api)] values = [val + offset for val in handler(self, data, self.legacy_api)]
data = self._write_dispatch[typ](self, *values) data = self._write_dispatch[typ](self, *values)
else: else:
value = self._pack("L", self._unpack("L", value)[0] + offset) value = self._pack(fmt, self._unpack(fmt, value)[0] + offset)
entries[stripoffsets] = tag, typ, count, value, data entries[stripoffsets] = tag, typ, count, value, data
# pass 2: write entries to file # pass 2: write entries to file
for tag, typ, count, value, data in entries: for tag, typ, count, value, data in entries:
logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data)) logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data))
result += self._pack("HHL4s", tag, typ, count, value) result += self._pack(
"HHQ8s" if self._bigtiff else "HHL4s", tag, typ, count, value
)
# -- overwrite here for multi-page -- # -- overwrite here for multi-page --
result += b"\0\0\0\0" # end of entries result += b"\0\0\0\0" # end of entries
@@ -1016,8 +1036,7 @@ class ImageFileDirectory_v2(_IFDv2Base):
def save(self, fp: IO[bytes]) -> int: def save(self, fp: IO[bytes]) -> int:
if fp.tell() == 0: # skip TIFF header on subsequent pages if fp.tell() == 0: # skip TIFF header on subsequent pages
# tiff header -- PIL always starts the first IFD at offset 8 fp.write(self._get_ifh())
fp.write(self._prefix + self._pack("HL", 42, 8))
offset = fp.tell() offset = fp.tell()
result = self.tobytes(offset) result = self.tobytes(offset)
@@ -1202,10 +1221,6 @@ class TiffImageFile(ImageFile.ImageFile):
def _seek(self, frame: int) -> None: def _seek(self, frame: int) -> None:
self.fp = self._fp self.fp = self._fp
# reset buffered io handle in case fp
# was passed to libtiff, invalidating the buffer
self.fp.tell()
while len(self._frame_pos) <= frame: while len(self._frame_pos) <= frame:
if not self.__next: if not self.__next:
msg = "no more images in TIFF file" msg = "no more images in TIFF file"
@@ -1289,10 +1304,6 @@ class TiffImageFile(ImageFile.ImageFile):
if not self.is_animated: if not self.is_animated:
self._close_exclusive_fp_after_loading = True self._close_exclusive_fp_after_loading = True
# reset buffered io handle in case fp
# was passed to libtiff, invalidating the buffer
self.fp.tell()
# load IFD data from fp before it is closed # load IFD data from fp before it is closed
exif = self.getexif() exif = self.getexif()
for key in TiffTags.TAGS_V2_GROUPS: for key in TiffTags.TAGS_V2_GROUPS:
@@ -1367,8 +1378,17 @@ class TiffImageFile(ImageFile.ImageFile):
logger.debug("have fileno, calling fileno version of the decoder.") logger.debug("have fileno, calling fileno version of the decoder.")
if not close_self_fp: if not close_self_fp:
self.fp.seek(0) self.fp.seek(0)
# Save and restore the file position, because libtiff will move it
# outside of the Python runtime, and that will confuse
# io.BufferedReader and possible others.
# NOTE: This must use os.lseek(), and not fp.tell()/fp.seek(),
# because the buffer read head already may not equal the actual
# file position, and fp.seek() may just adjust it's internal
# pointer and not actually seek the OS file handle.
pos = os.lseek(fp, 0, os.SEEK_CUR)
# 4 bytes, otherwise the trace might error out # 4 bytes, otherwise the trace might error out
n, err = decoder.decode(b"fpfp") n, err = decoder.decode(b"fpfp")
os.lseek(fp, pos, os.SEEK_SET)
else: else:
# we have something else. # we have something else.
logger.debug("don't have fileno or getvalue. just reading") logger.debug("don't have fileno or getvalue. just reading")
@@ -1419,8 +1439,12 @@ class TiffImageFile(ImageFile.ImageFile):
logger.debug("- YCbCr subsampling: %s", self.tag_v2.get(YCBCRSUBSAMPLING)) logger.debug("- YCbCr subsampling: %s", self.tag_v2.get(YCBCRSUBSAMPLING))
# size # size
xsize = self.tag_v2.get(IMAGEWIDTH) try:
ysize = self.tag_v2.get(IMAGELENGTH) xsize = self.tag_v2[IMAGEWIDTH]
ysize = self.tag_v2[IMAGELENGTH]
except KeyError as e:
msg = "Missing dimensions"
raise TypeError(msg) from e
if not isinstance(xsize, int) or not isinstance(ysize, int): if not isinstance(xsize, int) or not isinstance(ysize, int):
msg = "Invalid dimensions" msg = "Invalid dimensions"
raise ValueError(msg) raise ValueError(msg)
@@ -1542,17 +1566,6 @@ class TiffImageFile(ImageFile.ImageFile):
# fillorder==2 modes have a corresponding # fillorder==2 modes have a corresponding
# fillorder=1 mode # fillorder=1 mode
self._mode, rawmode = OPEN_INFO[key] self._mode, rawmode = OPEN_INFO[key]
# libtiff always returns the bytes in native order.
# we're expecting image byte order. So, if the rawmode
# contains I;16, we need to convert from native to image
# byte order.
if rawmode == "I;16":
rawmode = "I;16N"
if ";16B" in rawmode:
rawmode = rawmode.replace(";16B", ";16N")
if ";16L" in rawmode:
rawmode = rawmode.replace(";16L", ";16N")
# YCbCr images with new jpeg compression with pixels in one plane # YCbCr images with new jpeg compression with pixels in one plane
# unpacked straight into RGB values # unpacked straight into RGB values
if ( if (
@@ -1561,6 +1574,14 @@ class TiffImageFile(ImageFile.ImageFile):
and self._planar_configuration == 1 and self._planar_configuration == 1
): ):
rawmode = "RGB" rawmode = "RGB"
# libtiff always returns the bytes in native order.
# we're expecting image byte order. So, if the rawmode
# contains I;16, we need to convert from native to image
# byte order.
elif rawmode == "I;16":
rawmode = "I;16N"
elif rawmode.endswith(";16B") or rawmode.endswith(";16L"):
rawmode = rawmode[:-1] + "N"
# Offset in the tile tuple is 0, we go from 0,0 to # Offset in the tile tuple is 0, we go from 0,0 to
# w,h, and we only do this once -- eds # w,h, and we only do this once -- eds
@@ -1666,10 +1687,13 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
msg = f"cannot write mode {im.mode} as TIFF" msg = f"cannot write mode {im.mode} as TIFF"
raise OSError(msg) from e raise OSError(msg) from e
ifd = ImageFileDirectory_v2(prefix=prefix)
encoderinfo = im.encoderinfo encoderinfo = im.encoderinfo
encoderconfig = im.encoderconfig encoderconfig = im.encoderconfig
ifd = ImageFileDirectory_v2(prefix=prefix)
if encoderinfo.get("big_tiff"):
ifd._bigtiff = True
try: try:
compression = encoderinfo["compression"] compression = encoderinfo["compression"]
except KeyError: except KeyError:
@@ -1901,7 +1925,9 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
if not getattr(Image.core, "libtiff_support_custom_tags", False): if not getattr(Image.core, "libtiff_support_custom_tags", False):
continue continue
if tag in ifd.tagtype: if tag in TiffTags.TAGS_V2_GROUPS:
types[tag] = TiffTags.LONG8
elif tag in ifd.tagtype:
types[tag] = ifd.tagtype[tag] types[tag] = ifd.tagtype[tag]
elif not (isinstance(value, (int, float, str, bytes))): elif not (isinstance(value, (int, float, str, bytes))):
continue continue

View File

@@ -60,7 +60,6 @@ class WebPImageFile(ImageFile.ImageFile):
self.is_animated = self.n_frames > 1 self.is_animated = self.n_frames > 1
self._mode = "RGB" if mode == "RGBX" else mode self._mode = "RGB" if mode == "RGBX" else mode
self.rawmode = mode self.rawmode = mode
self.tile = []
# Attempt to read ICC / EXIF / XMP chunks from file # Attempt to read ICC / EXIF / XMP chunks from file
icc_profile = self._decoder.get_chunk("ICCP") icc_profile = self._decoder.get_chunk("ICCP")

View File

@@ -92,6 +92,9 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# get units per inch # get units per inch
self._inch = word(s, 14) self._inch = word(s, 14)
if self._inch == 0:
msg = "Invalid inch"
raise ValueError(msg)
# get bounding box # get bounding box
x0 = short(s, 6) x0 = short(s, 6)
@@ -128,7 +131,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
size = x1 - x0, y1 - y0 size = x1 - x0, y1 - y0
# calculate dots per inch from bbox and frame # calculate dots per inch from bbox and frame
xdpi = 2540.0 * (x1 - y0) / (frame[2] - frame[0]) xdpi = 2540.0 * (x1 - x0) / (frame[2] - frame[0])
ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1]) ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1])
self.info["wmf_bbox"] = x0, y0, x1, y1 self.info["wmf_bbox"] = x0, y0, x1, y1

View File

@@ -74,9 +74,7 @@ class XVThumbImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw("RGB", PALETTE) self.palette = ImagePalette.raw("RGB", PALETTE)
self.tile = [ self.tile = [
ImageFile._Tile( ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), self.mode)
"raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1)
)
] ]

View File

@@ -67,7 +67,7 @@ class XbmImageFile(ImageFile.ImageFile):
self._mode = "1" self._mode = "1"
self._size = xsize, ysize self._size = xsize, ysize
self.tile = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end(), None)] self.tile = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end())]
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
@@ -85,7 +85,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
fp.write(b"static char im_bits[] = {\n") fp.write(b"static char im_bits[] = {\n")
ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size, 0, None)]) ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size)])
fp.write(b"};\n") fp.write(b"};\n")

View File

@@ -101,9 +101,7 @@ class XpmImageFile(ImageFile.ImageFile):
self._mode = "P" self._mode = "P"
self.palette = ImagePalette.raw("RGB", b"".join(palette)) self.palette = ImagePalette.raw("RGB", b"".join(palette))
self.tile = [ self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), "P")]
ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))
]
def load_read(self, read_bytes: int) -> bytes: def load_read(self, read_bytes: int) -> bytes:
# #

Some files were not shown because too many files have changed in this diff Show More