mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	Merge branch 'main' into image_grab_wayland_kde
This commit is contained in:
		
						commit
						ec11f7aaed
					
				
							
								
								
									
										
											BIN
										
									
								
								Tests/images/drawing_emf_ref_72_144.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/drawing_emf_ref_72_144.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 984 B | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 533 B After Width: | Height: | Size: 533 B | 
|  | @ -224,3 +224,13 @@ def test_offset() -> None: | |||
|     # to exclude the palette size from the pixel data offset | ||||
|     with Image.open("Tests/images/pal8_offset.bmp") as im: | ||||
|         assert_image_equal_tofile(im, "Tests/images/bmp/g/pal8.bmp") | ||||
| 
 | ||||
| 
 | ||||
| def test_use_raw_alpha(monkeypatch: pytest.MonkeyPatch) -> None: | ||||
|     with Image.open("Tests/images/bmp/g/rgb32.bmp") as im: | ||||
|         assert im.info["compression"] == BmpImagePlugin.BmpImageFile.COMPRESSIONS["RAW"] | ||||
|         assert im.mode == "RGB" | ||||
| 
 | ||||
|     monkeypatch.setattr(BmpImagePlugin, "USE_RAW_ALPHA", True) | ||||
|     with Image.open("Tests/images/bmp/g/rgb32.bmp") as im: | ||||
|         assert im.mode == "RGBA" | ||||
|  |  | |||
|  | @ -1026,6 +1026,17 @@ class TestFileLibTiff(LibTiffTestCase): | |||
|         with Image.open("Tests/images/old-style-jpeg-compression.tif") as im: | ||||
|             assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png") | ||||
| 
 | ||||
|     def test_old_style_jpeg_orientation(self) -> None: | ||||
|         with open("Tests/images/old-style-jpeg-compression.tif", "rb") as fp: | ||||
|             data = fp.read() | ||||
| 
 | ||||
|             # Set EXIF Orientation to 2 | ||||
|             data = data[:102] + b"\x02" + data[103:] | ||||
| 
 | ||||
|             with Image.open(io.BytesIO(data)) as im: | ||||
|                 im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) | ||||
|             assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png") | ||||
| 
 | ||||
|     def test_open_missing_samplesperpixel(self) -> None: | ||||
|         with Image.open( | ||||
|             "Tests/images/old-style-jpeg-compression-no-samplesperpixel.tif" | ||||
|  |  | |||
|  | @ -43,6 +43,11 @@ def roundtrip(tmp_path: Path, mode: str) -> None: | |||
| 
 | ||||
|     im.save(outfile) | ||||
|     converted = open_with_magick(magick, tmp_path, outfile) | ||||
|     if mode == "P": | ||||
|         assert converted.mode == "P" | ||||
| 
 | ||||
|         im = im.convert("RGB") | ||||
|         converted = converted.convert("RGB") | ||||
|     assert_image_equal(converted, im) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -55,7 +60,6 @@ def test_monochrome(tmp_path: Path) -> None: | |||
|     roundtrip(tmp_path, mode) | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.xfail(reason="Palm P image is wrong") | ||||
| def test_p_mode(tmp_path: Path) -> None: | ||||
|     # Arrange | ||||
|     mode = "P" | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ import pytest | |||
| 
 | ||||
| from PIL import Image, ImageFile, WmfImagePlugin | ||||
| 
 | ||||
| from .helper import assert_image_similar_tofile, hopper | ||||
| from .helper import assert_image_equal_tofile, assert_image_similar_tofile, hopper | ||||
| 
 | ||||
| 
 | ||||
| def test_load_raw() -> None: | ||||
|  | @ -44,6 +44,15 @@ def test_load_zero_inch() -> None: | |||
|             pass | ||||
| 
 | ||||
| 
 | ||||
| def test_render() -> None: | ||||
|     with open("Tests/images/drawing.emf", "rb") as fp: | ||||
|         data = fp.read() | ||||
|     b = BytesIO(data[:808] + b"\x00" + data[809:]) | ||||
|     with Image.open(b) as im: | ||||
|         if hasattr(Image.core, "drawwmf"): | ||||
|             assert_image_equal_tofile(im, "Tests/images/drawing.emf") | ||||
| 
 | ||||
| 
 | ||||
| def test_register_handler(tmp_path: Path) -> None: | ||||
|     class TestHandler(ImageFile.StubHandler): | ||||
|         methodCalled = False | ||||
|  | @ -88,6 +97,20 @@ def test_load_set_dpi() -> None: | |||
| 
 | ||||
|             assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref_144.png", 2.1) | ||||
| 
 | ||||
|     with Image.open("Tests/images/drawing.emf") as im: | ||||
|         assert im.size == (1625, 1625) | ||||
| 
 | ||||
|         if not hasattr(Image.core, "drawwmf"): | ||||
|             return | ||||
|         im.load(im.info["dpi"]) | ||||
|         assert im.size == (1625, 1625) | ||||
| 
 | ||||
|     with Image.open("Tests/images/drawing.emf") as im: | ||||
|         im.load((72, 144)) | ||||
|         assert im.size == (82, 164) | ||||
| 
 | ||||
|         assert_image_equal_tofile(im, "Tests/images/drawing_emf_ref_72_144.png") | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.parametrize("ext", (".wmf", ".emf")) | ||||
| def test_save(ext: str, tmp_path: Path) -> None: | ||||
|  |  | |||
|  | @ -1704,7 +1704,7 @@ def test_discontiguous_corners_polygon() -> None: | |||
|         BLACK, | ||||
|     ) | ||||
|     expected = os.path.join(IMAGES_PATH, "discontiguous_corners_polygon.png") | ||||
|     assert_image_similar_tofile(img, expected, 1) | ||||
|     assert_image_equal_tofile(img, expected) | ||||
| 
 | ||||
| 
 | ||||
| def test_polygon2() -> None: | ||||
|  |  | |||
|  | @ -131,6 +131,26 @@ class TestImageFile: | |||
| 
 | ||||
|         assert_image_equal(im1, im2) | ||||
| 
 | ||||
|     def test_tile_size(self) -> None: | ||||
|         with open("Tests/images/hopper.tif", "rb") as im_fp: | ||||
|             data = im_fp.read() | ||||
| 
 | ||||
|         reads = [] | ||||
| 
 | ||||
|         class FP(BytesIO): | ||||
|             def read(self, size: int | None = None) -> bytes: | ||||
|                 reads.append(size) | ||||
|                 return super().read(size) | ||||
| 
 | ||||
|         fp = FP(data) | ||||
|         with Image.open(fp) as im: | ||||
|             assert len(im.tile) == 7 | ||||
| 
 | ||||
|             im.load() | ||||
| 
 | ||||
|         # Despite multiple tiles, assert only one tile caused a read of maxblock size | ||||
|         assert reads.count(im.decodermaxblock) == 1 | ||||
| 
 | ||||
|     def test_raise_oserror(self) -> None: | ||||
|         with pytest.warns(DeprecationWarning): | ||||
|             with pytest.raises(OSError): | ||||
|  |  | |||
|  | @ -4,21 +4,12 @@ | |||
| Security | ||||
| ======== | ||||
| 
 | ||||
| TODO | ||||
| ^^^^ | ||||
| Undefined shift when loading compressed DDS images | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| TODO | ||||
| 
 | ||||
| :cve:`YYYY-XXXXX`: TODO | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| TODO | ||||
| 
 | ||||
| Backwards Incompatible Changes | ||||
| ============================== | ||||
| 
 | ||||
| TODO | ||||
| ^^^^ | ||||
| When loading some compressed DDS formats, an integer was bitshifted by 24 places to | ||||
| generate the 32 bits of the lookup table. This was undefined behaviour, and has been | ||||
| present since Pillow 3.4.0. | ||||
| 
 | ||||
| Deprecations | ||||
| ============ | ||||
|  | @ -36,10 +27,14 @@ an :py:class:`PIL.ImageFile.ImageFile` instance. | |||
| API Changes | ||||
| =========== | ||||
| 
 | ||||
| TODO | ||||
| ^^^^ | ||||
| ``append_images`` no longer requires ``save_all`` | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| TODO | ||||
| Previously, ``save_all`` was required to in order to use ``append_images``. Now, | ||||
| ``save_all`` will default to ``True`` if ``append_images`` is not empty and the format | ||||
| supports saving multiple frames:: | ||||
| 
 | ||||
|     im.save("out.gif", append_images=ims) | ||||
| 
 | ||||
| API Additions | ||||
| ============= | ||||
|  | @ -73,11 +68,3 @@ Compressed DDS images can now be saved using a ``pixel_format`` argument. DXT1, | |||
| DXT5, BC2, BC3 and BC5 are supported:: | ||||
| 
 | ||||
|     im.save("out.dds", pixel_format="DXT1") | ||||
| 
 | ||||
| Other Changes | ||||
| ============= | ||||
| 
 | ||||
| TODO | ||||
| ^^^^ | ||||
| 
 | ||||
| TODO | ||||
|  |  | |||
|  | @ -48,6 +48,8 @@ BIT2MODE = { | |||
|     32: ("RGB", "BGRX"), | ||||
| } | ||||
| 
 | ||||
| USE_RAW_ALPHA = False | ||||
| 
 | ||||
| 
 | ||||
| def _accept(prefix: bytes) -> bool: | ||||
|     return prefix.startswith(b"BM") | ||||
|  | @ -242,7 +244,9 @@ class BmpImageFile(ImageFile.ImageFile): | |||
|                 msg = "Unsupported BMP bitfields layout" | ||||
|                 raise OSError(msg) | ||||
|         elif file_info["compression"] == self.COMPRESSIONS["RAW"]: | ||||
|             if file_info["bits"] == 32 and header == 22:  # 32-bit .cur offset | ||||
|             if file_info["bits"] == 32 and ( | ||||
|                 header == 22 or USE_RAW_ALPHA  # 32-bit .cur offset | ||||
|             ): | ||||
|                 raw_mode, self._mode = "BGRA", "RGBA" | ||||
|         elif file_info["compression"] in ( | ||||
|             self.COMPRESSIONS["RLE8"], | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ from __future__ import annotations | |||
| 
 | ||||
| from . import Image | ||||
| from ._binary import i32le as i32 | ||||
| from ._util import DeferredError | ||||
| from .PcxImagePlugin import PcxImageFile | ||||
| 
 | ||||
| MAGIC = 0x3ADE68B1  # QUIZ: what's this value, then? | ||||
|  | @ -66,6 +67,8 @@ class DcxImageFile(PcxImageFile): | |||
|     def seek(self, frame: int) -> None: | ||||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         self.frame = frame | ||||
|         self.fp = self._fp | ||||
|         self.fp.seek(self._offset[frame]) | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ from . import Image, ImageFile, ImagePalette | |||
| from ._binary import i16le as i16 | ||||
| from ._binary import i32le as i32 | ||||
| from ._binary import o8 | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| # | ||||
| # decoder | ||||
|  | @ -134,6 +135,8 @@ class FliImageFile(ImageFile.ImageFile): | |||
|             self._seek(f) | ||||
| 
 | ||||
|     def _seek(self, frame: int) -> None: | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         if frame == 0: | ||||
|             self.__frame = -1 | ||||
|             self._fp.seek(self.__rewind) | ||||
|  |  | |||
|  | @ -45,6 +45,7 @@ from . import ( | |||
| from ._binary import i16le as i16 | ||||
| from ._binary import o8 | ||||
| from ._binary import o16le as o16 | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| if TYPE_CHECKING: | ||||
|     from . import _imaging | ||||
|  | @ -167,6 +168,8 @@ class GifImageFile(ImageFile.ImageFile): | |||
|                 raise EOFError(msg) from e | ||||
| 
 | ||||
|     def _seek(self, frame: int, update_image: bool = True) -> None: | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         if frame == 0: | ||||
|             # rewind | ||||
|             self.__offset = 0 | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ import re | |||
| from typing import IO, Any | ||||
| 
 | ||||
| from . import Image, ImageFile, ImagePalette | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Standard tags | ||||
|  | @ -290,6 +291,8 @@ class ImImageFile(ImageFile.ImageFile): | |||
|     def seek(self, frame: int) -> None: | ||||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
| 
 | ||||
|         self.frame = frame | ||||
| 
 | ||||
|  |  | |||
|  | @ -621,6 +621,8 @@ class Image: | |||
|         more information. | ||||
|         """ | ||||
|         if getattr(self, "map", None): | ||||
|             if sys.platform == "win32" and hasattr(sys, "pypy_version_info"): | ||||
|                 self.map.close() | ||||
|             self.map: mmap.mmap | None = None | ||||
| 
 | ||||
|         # Instead of simply setting to None, we're setting up a | ||||
|  |  | |||
|  | @ -34,7 +34,6 @@ import itertools | |||
| import logging | ||||
| import os | ||||
| import struct | ||||
| import sys | ||||
| from typing import IO, TYPE_CHECKING, Any, NamedTuple, cast | ||||
| 
 | ||||
| from . import ExifTags, Image | ||||
|  | @ -167,7 +166,7 @@ class ImageFile(Image.Image): | |||
|         pass | ||||
| 
 | ||||
|     def _close_fp(self): | ||||
|         if getattr(self, "_fp", False): | ||||
|         if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError): | ||||
|             if self._fp != self.fp: | ||||
|                 self._fp.close() | ||||
|             self._fp = DeferredError(ValueError("Operation on closed image")) | ||||
|  | @ -278,8 +277,6 @@ class ImageFile(Image.Image): | |||
| 
 | ||||
|         self.map: mmap.mmap | None = None | ||||
|         use_mmap = self.filename and len(self.tile) == 1 | ||||
|         # As of pypy 2.1.0, memory mapping was failing here. | ||||
|         use_mmap = use_mmap and not hasattr(sys, "pypy_version_info") | ||||
| 
 | ||||
|         readonly = 0 | ||||
| 
 | ||||
|  | @ -345,7 +342,7 @@ class ImageFile(Image.Image): | |||
|                     self.tile, lambda tile: (tile[0], tile[1], tile[3]) | ||||
|                 ) | ||||
|             ] | ||||
|             for decoder_name, extents, offset, args in self.tile: | ||||
|             for i, (decoder_name, extents, offset, args) in enumerate(self.tile): | ||||
|                 seek(offset) | ||||
|                 decoder = Image._getdecoder( | ||||
|                     self.mode, decoder_name, args, self.decoderconfig | ||||
|  | @ -358,8 +355,13 @@ class ImageFile(Image.Image): | |||
|                     else: | ||||
|                         b = prefix | ||||
|                         while True: | ||||
|                             read_bytes = self.decodermaxblock | ||||
|                             if i + 1 < len(self.tile): | ||||
|                                 next_offset = self.tile[i + 1].offset | ||||
|                                 if next_offset > offset: | ||||
|                                     read_bytes = next_offset - offset | ||||
|                             try: | ||||
|                                 s = read(self.decodermaxblock) | ||||
|                                 s = read(read_bytes) | ||||
|                             except (IndexError, struct.error) as e: | ||||
|                                 # truncated png/gif | ||||
|                                 if LOAD_TRUNCATED_IMAGES: | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ from . import ( | |||
|     TiffImagePlugin, | ||||
| ) | ||||
| from ._binary import o32le | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| 
 | ||||
| def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: | ||||
|  | @ -125,11 +126,15 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): | |||
|         self.readonly = 1 | ||||
| 
 | ||||
|     def load_seek(self, pos: int) -> None: | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         self._fp.seek(pos) | ||||
| 
 | ||||
|     def seek(self, frame: int) -> None: | ||||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         self.fp = self._fp | ||||
|         self.offset = self.__mpoffsets[frame] | ||||
| 
 | ||||
|  |  | |||
|  | @ -116,9 +116,6 @@ _COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00} | |||
| 
 | ||||
| def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: | ||||
|     if im.mode == "P": | ||||
|         # we assume this is a color Palm image with the standard colormap, | ||||
|         # unless the "info" dict has a "custom-colormap" field | ||||
| 
 | ||||
|         rawmode = "P" | ||||
|         bpp = 8 | ||||
|         version = 1 | ||||
|  | @ -172,12 +169,11 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: | |||
|     compression_type = _COMPRESSION_TYPES["none"] | ||||
| 
 | ||||
|     flags = 0 | ||||
|     if im.mode == "P" and "custom-colormap" in im.info: | ||||
|         assert im.palette is not None | ||||
|         flags = flags & _FLAGS["custom-colormap"] | ||||
|         colormapsize = 4 * 256 + 2 | ||||
|         colormapmode = im.palette.mode | ||||
|         colormap = im.getdata().getpalette() | ||||
|     if im.mode == "P": | ||||
|         flags |= _FLAGS["custom-colormap"] | ||||
|         colormap = im.im.getpalette() | ||||
|         colors = len(colormap) // 3 | ||||
|         colormapsize = 4 * colors + 2 | ||||
|     else: | ||||
|         colormapsize = 0 | ||||
| 
 | ||||
|  | @ -196,22 +192,11 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: | |||
| 
 | ||||
|     # now write colormap if necessary | ||||
| 
 | ||||
|     if colormapsize > 0: | ||||
|         fp.write(o16b(256)) | ||||
|         for i in range(256): | ||||
|     if colormapsize: | ||||
|         fp.write(o16b(colors)) | ||||
|         for i in range(colors): | ||||
|             fp.write(o8(i)) | ||||
|             if colormapmode == "RGB": | ||||
|                 fp.write( | ||||
|                     o8(colormap[3 * i]) | ||||
|                     + o8(colormap[3 * i + 1]) | ||||
|                     + o8(colormap[3 * i + 2]) | ||||
|                 ) | ||||
|             elif colormapmode == "RGBA": | ||||
|                 fp.write( | ||||
|                     o8(colormap[4 * i]) | ||||
|                     + o8(colormap[4 * i + 1]) | ||||
|                     + o8(colormap[4 * i + 2]) | ||||
|                 ) | ||||
|             fp.write(colormap[3 * i : 3 * i + 3]) | ||||
| 
 | ||||
|     # now convert data to raw form | ||||
|     ImageFile._save( | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ from ._binary import i32be as i32 | |||
| from ._binary import o8 | ||||
| from ._binary import o16be as o16 | ||||
| from ._binary import o32be as o32 | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| if TYPE_CHECKING: | ||||
|     from . import _imaging | ||||
|  | @ -869,6 +870,8 @@ class PngImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|     def _seek(self, frame: int, rewind: bool = False) -> None: | ||||
|         assert self.png is not None | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
| 
 | ||||
|         self.dispose: _imaging.ImagingCore | None | ||||
|         dispose_extent = None | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ from ._binary import i16be as i16 | |||
| from ._binary import i32be as i32 | ||||
| from ._binary import si16be as si16 | ||||
| from ._binary import si32be as si32 | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| MODES = { | ||||
|     # (photoshop mode, bits) -> (pil mode, required channels) | ||||
|  | @ -148,6 +149,8 @@ class PsdImageFile(ImageFile.ImageFile): | |||
|     ) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: | ||||
|         layers = [] | ||||
|         if self._layers_position is not None: | ||||
|             if isinstance(self._fp, DeferredError): | ||||
|                 raise self._fp.ex | ||||
|             self._fp.seek(self._layers_position) | ||||
|             _layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size)) | ||||
|             layers = _layerinfo(_layer_data, self._layers_size) | ||||
|  | @ -167,6 +170,8 @@ class PsdImageFile(ImageFile.ImageFile): | |||
|     def seek(self, layer: int) -> None: | ||||
|         if not self._seek_check(layer): | ||||
|             return | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
| 
 | ||||
|         # seek to given layer (1..max) | ||||
|         _, mode, _, tile = self.layers[layer - 1] | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ import sys | |||
| from typing import IO, TYPE_CHECKING, Any, cast | ||||
| 
 | ||||
| from . import Image, ImageFile | ||||
| from ._util import DeferredError | ||||
| 
 | ||||
| 
 | ||||
| def isInt(f: Any) -> int: | ||||
|  | @ -178,6 +179,8 @@ class SpiderImageFile(ImageFile.ImageFile): | |||
|             raise EOFError(msg) | ||||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) | ||||
|         self.fp = self._fp | ||||
|         self.fp.seek(self.stkoffset) | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ from ._binary import i32be as i32 | |||
| from ._binary import o8 | ||||
| from ._deprecate import deprecate | ||||
| from ._typing import StrOrBytesPath | ||||
| from ._util import is_path | ||||
| from ._util import DeferredError, is_path | ||||
| from .TiffTags import TYPES | ||||
| 
 | ||||
| if TYPE_CHECKING: | ||||
|  | @ -1222,6 +1222,8 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|             self._im = None | ||||
| 
 | ||||
|     def _seek(self, frame: int) -> None: | ||||
|         if isinstance(self._fp, DeferredError): | ||||
|             raise self._fp.ex | ||||
|         self.fp = self._fp | ||||
| 
 | ||||
|         while len(self._frame_pos) <= frame: | ||||
|  |  | |||
|  | @ -80,8 +80,6 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
|     format_description = "Windows Metafile" | ||||
| 
 | ||||
|     def _open(self) -> None: | ||||
|         self._inch = None | ||||
| 
 | ||||
|         # check placable header | ||||
|         s = self.fp.read(80) | ||||
| 
 | ||||
|  | @ -89,10 +87,11 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
|             # placeable windows metafile | ||||
| 
 | ||||
|             # get units per inch | ||||
|             self._inch = word(s, 14) | ||||
|             if self._inch == 0: | ||||
|             inch = word(s, 14) | ||||
|             if inch == 0: | ||||
|                 msg = "Invalid inch" | ||||
|                 raise ValueError(msg) | ||||
|             self._inch: tuple[float, float] = inch, inch | ||||
| 
 | ||||
|             # get bounding box | ||||
|             x0 = short(s, 6) | ||||
|  | @ -103,8 +102,8 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
|             # normalize size to 72 dots per inch | ||||
|             self.info["dpi"] = 72 | ||||
|             size = ( | ||||
|                 (x1 - x0) * self.info["dpi"] // self._inch, | ||||
|                 (y1 - y0) * self.info["dpi"] // self._inch, | ||||
|                 (x1 - x0) * self.info["dpi"] // inch, | ||||
|                 (y1 - y0) * self.info["dpi"] // inch, | ||||
|             ) | ||||
| 
 | ||||
|             self.info["wmf_bbox"] = x0, y0, x1, y1 | ||||
|  | @ -138,6 +137,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
|                 self.info["dpi"] = xdpi | ||||
|             else: | ||||
|                 self.info["dpi"] = xdpi, ydpi | ||||
|             self._inch = xdpi, ydpi | ||||
| 
 | ||||
|         else: | ||||
|             msg = "Unsupported file format" | ||||
|  | @ -153,13 +153,17 @@ class WmfStubImageFile(ImageFile.StubImageFile): | |||
|     def _load(self) -> ImageFile.StubHandler | None: | ||||
|         return _handler | ||||
| 
 | ||||
|     def load(self, dpi: int | None = None) -> Image.core.PixelAccess | None: | ||||
|         if dpi is not None and self._inch is not None: | ||||
|     def load( | ||||
|         self, dpi: float | tuple[float, float] | None = None | ||||
|     ) -> Image.core.PixelAccess | None: | ||||
|         if dpi is not None: | ||||
|             self.info["dpi"] = dpi | ||||
|             x0, y0, x1, y1 = self.info["wmf_bbox"] | ||||
|             if not isinstance(dpi, tuple): | ||||
|                 dpi = dpi, dpi | ||||
|             self._size = ( | ||||
|                 (x1 - x0) * self.info["dpi"] // self._inch, | ||||
|                 (y1 - y0) * self.info["dpi"] // self._inch, | ||||
|                 int((x1 - x0) * dpi[0] / self._inch[0]), | ||||
|                 int((y1 - y0) * dpi[1] / self._inch[1]), | ||||
|             ) | ||||
|         return super().load() | ||||
| 
 | ||||
|  |  | |||
|  | @ -687,6 +687,14 @@ PyImaging_EventLoopWin32(PyObject *self, PyObject *args) { | |||
| 
 | ||||
| #define GET32(p, o) ((DWORD *)(p + o))[0] | ||||
| 
 | ||||
| static int CALLBACK | ||||
| enhMetaFileProc( | ||||
|     HDC hdc, HANDLETABLE *lpht, const ENHMETARECORD *lpmr, int nHandles, LPARAM data | ||||
| ) { | ||||
|     PlayEnhMetaFileRecord(hdc, lpht, lpmr, nHandles); | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| PyObject * | ||||
| PyImaging_DrawWmf(PyObject *self, PyObject *args) { | ||||
|     HBITMAP bitmap; | ||||
|  | @ -767,10 +775,7 @@ PyImaging_DrawWmf(PyObject *self, PyObject *args) { | |||
|     /* FIXME: make background transparent? configurable? */ | ||||
|     FillRect(dc, &rect, GetStockObject(WHITE_BRUSH)); | ||||
| 
 | ||||
|     if (!PlayEnhMetaFile(dc, meta, &rect)) { | ||||
|         PyErr_SetString(PyExc_OSError, "cannot render metafile"); | ||||
|         goto error; | ||||
|     } | ||||
|     EnumEnhMetaFile(dc, meta, enhMetaFileProc, NULL, &rect); | ||||
| 
 | ||||
|     /* step 4: extract bits from bitmap */ | ||||
| 
 | ||||
|  |  | |||
|  | @ -501,55 +501,49 @@ polygon_generic( | |||
|                     // Needed to draw consistent polygons
 | ||||
|                     xx[j] = xx[j - 1]; | ||||
|                     j++; | ||||
|                 } else if (current->dx != 0 && j % 2 == 1 && | ||||
|                            roundf(xx[j - 1]) == xx[j - 1]) { | ||||
|                 } else if ((ymin == current->ymin || ymin == current->ymax) && | ||||
|                            current->dx != 0) { | ||||
|                     // Connect discontiguous corners
 | ||||
|                     for (k = 0; k < i; k++) { | ||||
|                         Edge *other_edge = edge_table[k]; | ||||
|                         if ((current->dx > 0 && other_edge->dx <= 0) || | ||||
|                             (current->dx < 0 && other_edge->dx >= 0)) { | ||||
|                         if ((ymin != other_edge->ymin && ymin != other_edge->ymax) || | ||||
|                             other_edge->dx == 0) { | ||||
|                             continue; | ||||
|                         } | ||||
|                         // Check if the two edges join to make a corner
 | ||||
|                         if (xx[j - 1] == | ||||
|                             (ymin - other_edge->y0) * other_edge->dx + other_edge->x0) { | ||||
|                         if (roundf(xx[j - 1]) == | ||||
|                             roundf( | ||||
|                                 (ymin - other_edge->y0) * other_edge->dx + | ||||
|                                 other_edge->x0 | ||||
|                             )) { | ||||
|                             // Determine points from the edges on the next row
 | ||||
|                             // Or if this is the last row, check the previous row
 | ||||
|                             int offset = ymin == ymax ? -1 : 1; | ||||
|                             int offset = ymin == current->ymax ? -1 : 1; | ||||
|                             adjacent_line_x = | ||||
|                                 (ymin + offset - current->y0) * current->dx + | ||||
|                                 current->x0; | ||||
|                             adjacent_line_x_other_edge = | ||||
|                                 (ymin + offset - other_edge->y0) * other_edge->dx + | ||||
|                                 other_edge->x0; | ||||
|                             if (ymin == current->ymax) { | ||||
|                                 if (current->dx > 0) { | ||||
|                                     xx[k] = | ||||
|                                         fmax( | ||||
|                             if (ymin + offset >= other_edge->ymin && | ||||
|                                 ymin + offset <= other_edge->ymax) { | ||||
|                                 adjacent_line_x_other_edge = | ||||
|                                     (ymin + offset - other_edge->y0) * other_edge->dx + | ||||
|                                     other_edge->x0; | ||||
|                                 if (xx[j - 1] > adjacent_line_x + 1 && | ||||
|                                     xx[j - 1] > adjacent_line_x_other_edge + 1) { | ||||
|                                     xx[j - 1] = | ||||
|                                         roundf(fmax( | ||||
|                                             adjacent_line_x, adjacent_line_x_other_edge | ||||
|                                         ) + | ||||
|                                         )) + | ||||
|                                         1; | ||||
|                                 } else { | ||||
|                                     xx[k] = | ||||
|                                         fmin( | ||||
|                                 } else if (xx[j - 1] < adjacent_line_x - 1 && | ||||
|                                            xx[j - 1] < adjacent_line_x_other_edge - 1) { | ||||
|                                     xx[j - 1] = | ||||
|                                         roundf(fmin( | ||||
|                                             adjacent_line_x, adjacent_line_x_other_edge | ||||
|                                         ) - | ||||
|                                         1; | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 if (current->dx > 0) { | ||||
|                                     xx[k] = fmin( | ||||
|                                         adjacent_line_x, adjacent_line_x_other_edge | ||||
|                                     ); | ||||
|                                 } else { | ||||
|                                     xx[k] = | ||||
|                                         fmax( | ||||
|                                             adjacent_line_x, adjacent_line_x_other_edge | ||||
|                                         ) + | ||||
|                                         )) - | ||||
|                                         1; | ||||
|                                 } | ||||
|                                 break; | ||||
|                             } | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  |  | |||
|  | @ -299,6 +299,7 @@ _decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) { | |||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     img.orientation = ORIENTATION_TOPLEFT; | ||||
|     img.req_orientation = ORIENTATION_TOPLEFT; | ||||
|     img.col_offset = 0; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user