mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Merge branch 'main' into iptc
This commit is contained in:
		
						commit
						dcfce9487e
					
				| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
repos:
 | 
			
		||||
  - repo: https://github.com/psf/black
 | 
			
		||||
    rev: 23.3.0
 | 
			
		||||
    rev: 23.7.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args: [--target-version=py38]
 | 
			
		||||
| 
						 | 
				
			
			@ -23,13 +23,13 @@ repos:
 | 
			
		|||
      - id: yesqa
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/Lucas-C/pre-commit-hooks
 | 
			
		||||
    rev: v1.5.1
 | 
			
		||||
    rev: v1.5.3
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: remove-tabs
 | 
			
		||||
        exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/PyCQA/flake8
 | 
			
		||||
    rev: 6.0.0
 | 
			
		||||
    rev: 6.1.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: flake8
 | 
			
		||||
        additional_dependencies:
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ repos:
 | 
			
		|||
      - id: sphinx-lint
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/tox-dev/pyproject-fmt
 | 
			
		||||
    rev: 0.12.1
 | 
			
		||||
    rev: 0.13.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyproject-fmt
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								CHANGES.rst
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -5,6 +5,21 @@ Changelog (Pillow)
 | 
			
		|||
10.1.0 (unreleased)
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
- Read WebP duration after opening #7311
 | 
			
		||||
  [k128, radarhere]
 | 
			
		||||
 | 
			
		||||
- Allow "loop=None" when saving GIF images #7329
 | 
			
		||||
  [radarhere]
 | 
			
		||||
 | 
			
		||||
- Fixed transparency when saving P mode images to PDF #7323
 | 
			
		||||
  [radarhere]
 | 
			
		||||
 | 
			
		||||
- Added saving LA images as PDFs #7299
 | 
			
		||||
  [radarhere]
 | 
			
		||||
 | 
			
		||||
- Set SMaskInData to 1 for PDFs with alpha #7316, #7317
 | 
			
		||||
  [radarhere]
 | 
			
		||||
 | 
			
		||||
- Changed Image mode property to be read-only by default #7307
 | 
			
		||||
  [radarhere]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -875,6 +875,14 @@ def test_identical_frames_to_single_frame(duration, tmp_path):
 | 
			
		|||
        assert reread.info["duration"] == 8500
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_loop_none(tmp_path):
 | 
			
		||||
    out = str(tmp_path / "temp.gif")
 | 
			
		||||
    im = Image.new("L", (100, 100), "#000")
 | 
			
		||||
    im.save(out, loop=None)
 | 
			
		||||
    with Image.open(out) as reread:
 | 
			
		||||
        assert "loop" not in reread.info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_number_of_loops(tmp_path):
 | 
			
		||||
    number_of_loops = 2
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,22 @@ def test_save_alpha(tmp_path, mode):
 | 
			
		|||
    helper_save_as_pdf(tmp_path, mode)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_p_alpha(tmp_path):
 | 
			
		||||
    # Arrange
 | 
			
		||||
    outfile = str(tmp_path / "temp.pdf")
 | 
			
		||||
    with Image.open("Tests/images/pil123p.png") as im:
 | 
			
		||||
        assert im.mode == "P"
 | 
			
		||||
        assert isinstance(im.info["transparency"], bytes)
 | 
			
		||||
 | 
			
		||||
        # Act
 | 
			
		||||
        im.save(outfile)
 | 
			
		||||
 | 
			
		||||
    # Assert
 | 
			
		||||
    with open(outfile, "rb") as fp:
 | 
			
		||||
        contents = fp.read()
 | 
			
		||||
    assert b"\n/SMask " in contents
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_monochrome(tmp_path):
 | 
			
		||||
    # Arrange
 | 
			
		||||
    mode = "1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ class TestFilePng:
 | 
			
		|||
 | 
			
		||||
    def test_sanity(self, tmp_path):
 | 
			
		||||
        # internal version number
 | 
			
		||||
        assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib"))
 | 
			
		||||
        assert re.search(r"\d+(\.\d+){1,3}$", features.version_codec("zlib"))
 | 
			
		||||
 | 
			
		||||
        test_file = str(tmp_path / "temp.png")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -233,5 +233,4 @@ class TestFileWebp:
 | 
			
		|||
            im.save(out_webp, save_all=True)
 | 
			
		||||
 | 
			
		||||
        with Image.open(out_webp) as reloaded:
 | 
			
		||||
            reloaded.load()
 | 
			
		||||
            assert reloaded.info["duration"] == 1000
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -661,15 +661,15 @@ class TestImage:
 | 
			
		|||
        blank_p.palette = None
 | 
			
		||||
        blank_pa.palette = None
 | 
			
		||||
 | 
			
		||||
        def _make_new(base_image, im, palette_result=None):
 | 
			
		||||
            new_im = base_image._new(im)
 | 
			
		||||
            assert new_im.mode == im.mode
 | 
			
		||||
            assert new_im.size == im.size
 | 
			
		||||
            assert new_im.info == base_image.info
 | 
			
		||||
        def _make_new(base_image, image, palette_result=None):
 | 
			
		||||
            new_image = base_image._new(image.im)
 | 
			
		||||
            assert new_image.mode == image.mode
 | 
			
		||||
            assert new_image.size == image.size
 | 
			
		||||
            assert new_image.info == base_image.info
 | 
			
		||||
            if palette_result is not None:
 | 
			
		||||
                assert new_im.palette.tobytes() == palette_result.tobytes()
 | 
			
		||||
                assert new_image.palette.tobytes() == palette_result.tobytes()
 | 
			
		||||
            else:
 | 
			
		||||
                assert new_im.palette is None
 | 
			
		||||
                assert new_image.palette is None
 | 
			
		||||
 | 
			
		||||
        _make_new(im, im_p, ImagePalette.ImagePalette(list(range(256)) * 3))
 | 
			
		||||
        _make_new(im_p, im, None)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -253,7 +253,7 @@ their :py:attr:`~PIL.Image.Image.info` values.
 | 
			
		|||
 | 
			
		||||
**loop**
 | 
			
		||||
    Integer number of times the GIF should loop. 0 means that it will loop
 | 
			
		||||
    forever. By default, the image will not loop.
 | 
			
		||||
    forever. If omitted or ``None``, the image will not loop.
 | 
			
		||||
 | 
			
		||||
**comment**
 | 
			
		||||
    A comment about the image.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,10 +83,9 @@ Install Pillow with :command:`pip`::
 | 
			
		|||
.. tab:: Windows
 | 
			
		||||
 | 
			
		||||
    We provide Pillow binaries for Windows compiled for the matrix of
 | 
			
		||||
    supported Pythons in both 32 and 64-bit versions in the wheel format.
 | 
			
		||||
    These binaries include support for all optional libraries except
 | 
			
		||||
    libimagequant and libxcb. Raqm support requires
 | 
			
		||||
    FriBiDi to be installed separately::
 | 
			
		||||
    supported Pythons in 64-bit versions in the wheel format. These binaries include
 | 
			
		||||
    support for all optional libraries except libimagequant and libxcb. Raqm support
 | 
			
		||||
    requires FriBiDi to be installed separately::
 | 
			
		||||
 | 
			
		||||
        python3 -m pip install --upgrade pip
 | 
			
		||||
        python3 -m pip install --upgrade Pillow
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -206,4 +206,4 @@ Support reading signed 8-bit TIFF images
 | 
			
		|||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
TIFF images with signed integer data, 8 bits per sample and a photometric
 | 
			
		||||
interpretaton of BlackIsZero can now be read.
 | 
			
		||||
interpretation of BlackIsZero can now be read.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -912,7 +912,7 @@ def _get_global_header(im, info):
 | 
			
		|||
        info
 | 
			
		||||
        and (
 | 
			
		||||
            "transparency" in info
 | 
			
		||||
            or "loop" in info
 | 
			
		||||
            or info.get("loop") is not None
 | 
			
		||||
            or info.get("duration")
 | 
			
		||||
            or info.get("comment")
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -937,7 +937,7 @@ def _get_global_header(im, info):
 | 
			
		|||
        # Global Color Table
 | 
			
		||||
        _get_header_palette(palette_bytes),
 | 
			
		||||
    ]
 | 
			
		||||
    if "loop" in info:
 | 
			
		||||
    if info.get("loop") is not None:
 | 
			
		||||
        header.append(
 | 
			
		||||
            b"!"
 | 
			
		||||
            + o8(255)  # extension intro
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -247,7 +247,7 @@ def eval(expression, _dict={}, **kw):
 | 
			
		|||
 | 
			
		||||
    def scan(code):
 | 
			
		||||
        for const in code.co_consts:
 | 
			
		||||
            if type(const) == type(compiled_code):
 | 
			
		||||
            if type(const) is type(compiled_code):
 | 
			
		||||
                scan(const)
 | 
			
		||||
 | 
			
		||||
        for name in code.co_names:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,132 @@ def _save_all(im, fp, filename):
 | 
			
		|||
# (Internal) Image save plugin for the PDF format.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _write_image(im, filename, existing_pdf, image_refs):
 | 
			
		||||
    # FIXME: Should replace ASCIIHexDecode with RunLengthDecode
 | 
			
		||||
    # (packbits) or LZWDecode (tiff/lzw compression).  Note that
 | 
			
		||||
    # PDF 1.2 also supports Flatedecode (zip compression).
 | 
			
		||||
 | 
			
		||||
    params = None
 | 
			
		||||
    decode = None
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # Get image characteristics
 | 
			
		||||
 | 
			
		||||
    width, height = im.size
 | 
			
		||||
 | 
			
		||||
    dict_obj = {"BitsPerComponent": 8}
 | 
			
		||||
    if im.mode == "1":
 | 
			
		||||
        if features.check("libtiff"):
 | 
			
		||||
            filter = "CCITTFaxDecode"
 | 
			
		||||
            dict_obj["BitsPerComponent"] = 1
 | 
			
		||||
            params = PdfParser.PdfArray(
 | 
			
		||||
                [
 | 
			
		||||
                    PdfParser.PdfDict(
 | 
			
		||||
                        {
 | 
			
		||||
                            "K": -1,
 | 
			
		||||
                            "BlackIs1": True,
 | 
			
		||||
                            "Columns": width,
 | 
			
		||||
                            "Rows": height,
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            filter = "DCTDecode"
 | 
			
		||||
        dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray")
 | 
			
		||||
        procset = "ImageB"  # grayscale
 | 
			
		||||
    elif im.mode == "L":
 | 
			
		||||
        filter = "DCTDecode"
 | 
			
		||||
        # params = f"<< /Predictor 15 /Columns {width-2} >>"
 | 
			
		||||
        dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray")
 | 
			
		||||
        procset = "ImageB"  # grayscale
 | 
			
		||||
    elif im.mode == "LA":
 | 
			
		||||
        filter = "JPXDecode"
 | 
			
		||||
        # params = f"<< /Predictor 15 /Columns {width-2} >>"
 | 
			
		||||
        procset = "ImageB"  # grayscale
 | 
			
		||||
        dict_obj["SMaskInData"] = 1
 | 
			
		||||
    elif im.mode == "P":
 | 
			
		||||
        filter = "ASCIIHexDecode"
 | 
			
		||||
        palette = im.getpalette()
 | 
			
		||||
        dict_obj["ColorSpace"] = [
 | 
			
		||||
            PdfParser.PdfName("Indexed"),
 | 
			
		||||
            PdfParser.PdfName("DeviceRGB"),
 | 
			
		||||
            255,
 | 
			
		||||
            PdfParser.PdfBinary(palette),
 | 
			
		||||
        ]
 | 
			
		||||
        procset = "ImageI"  # indexed color
 | 
			
		||||
 | 
			
		||||
        if "transparency" in im.info:
 | 
			
		||||
            smask = im.convert("LA").getchannel("A")
 | 
			
		||||
            smask.encoderinfo = {}
 | 
			
		||||
 | 
			
		||||
            image_ref = _write_image(smask, filename, existing_pdf, image_refs)[0]
 | 
			
		||||
            dict_obj["SMask"] = image_ref
 | 
			
		||||
    elif im.mode == "RGB":
 | 
			
		||||
        filter = "DCTDecode"
 | 
			
		||||
        dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB")
 | 
			
		||||
        procset = "ImageC"  # color images
 | 
			
		||||
    elif im.mode == "RGBA":
 | 
			
		||||
        filter = "JPXDecode"
 | 
			
		||||
        procset = "ImageC"  # color images
 | 
			
		||||
        dict_obj["SMaskInData"] = 1
 | 
			
		||||
    elif im.mode == "CMYK":
 | 
			
		||||
        filter = "DCTDecode"
 | 
			
		||||
        dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceCMYK")
 | 
			
		||||
        procset = "ImageC"  # color images
 | 
			
		||||
        decode = [1, 0, 1, 0, 1, 0, 1, 0]
 | 
			
		||||
    else:
 | 
			
		||||
        msg = f"cannot save mode {im.mode}"
 | 
			
		||||
        raise ValueError(msg)
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # image
 | 
			
		||||
 | 
			
		||||
    op = io.BytesIO()
 | 
			
		||||
 | 
			
		||||
    if filter == "ASCIIHexDecode":
 | 
			
		||||
        ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
    elif filter == "CCITTFaxDecode":
 | 
			
		||||
        im.save(
 | 
			
		||||
            op,
 | 
			
		||||
            "TIFF",
 | 
			
		||||
            compression="group4",
 | 
			
		||||
            # use a single strip
 | 
			
		||||
            strip_size=math.ceil(width / 8) * height,
 | 
			
		||||
        )
 | 
			
		||||
    elif filter == "DCTDecode":
 | 
			
		||||
        Image.SAVE["JPEG"](im, op, filename)
 | 
			
		||||
    elif filter == "JPXDecode":
 | 
			
		||||
        del dict_obj["BitsPerComponent"]
 | 
			
		||||
        Image.SAVE["JPEG2000"](im, op, filename)
 | 
			
		||||
    else:
 | 
			
		||||
        msg = f"unsupported PDF filter ({filter})"
 | 
			
		||||
        raise ValueError(msg)
 | 
			
		||||
 | 
			
		||||
    stream = op.getvalue()
 | 
			
		||||
    if filter == "CCITTFaxDecode":
 | 
			
		||||
        stream = stream[8:]
 | 
			
		||||
        filter = PdfParser.PdfArray([PdfParser.PdfName(filter)])
 | 
			
		||||
    else:
 | 
			
		||||
        filter = PdfParser.PdfName(filter)
 | 
			
		||||
 | 
			
		||||
    image_ref = image_refs.pop(0)
 | 
			
		||||
    existing_pdf.write_obj(
 | 
			
		||||
        image_ref,
 | 
			
		||||
        stream=stream,
 | 
			
		||||
        Type=PdfParser.PdfName("XObject"),
 | 
			
		||||
        Subtype=PdfParser.PdfName("Image"),
 | 
			
		||||
        Width=width,  # * 72.0 / x_resolution,
 | 
			
		||||
        Height=height,  # * 72.0 / y_resolution,
 | 
			
		||||
        Filter=filter,
 | 
			
		||||
        Decode=decode,
 | 
			
		||||
        DecodeParms=params,
 | 
			
		||||
        **dict_obj,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return image_ref, procset
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _save(im, fp, filename, save_all=False):
 | 
			
		||||
    is_appending = im.encoderinfo.get("append", False)
 | 
			
		||||
    if is_appending:
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +235,9 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
        number_of_pages += im_number_of_pages
 | 
			
		||||
        for i in range(im_number_of_pages):
 | 
			
		||||
            image_refs.append(existing_pdf.next_object_id(0))
 | 
			
		||||
            if im.mode == "P" and "transparency" in im.info:
 | 
			
		||||
                image_refs.append(existing_pdf.next_object_id(0))
 | 
			
		||||
 | 
			
		||||
            page_refs.append(existing_pdf.next_object_id(0))
 | 
			
		||||
            contents_refs.append(existing_pdf.next_object_id(0))
 | 
			
		||||
            existing_pdf.pages.append(page_refs[-1])
 | 
			
		||||
| 
						 | 
				
			
			@ -121,123 +250,7 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
    for im_sequence in ims:
 | 
			
		||||
        im_pages = ImageSequence.Iterator(im_sequence) if save_all else [im_sequence]
 | 
			
		||||
        for im in im_pages:
 | 
			
		||||
            # FIXME: Should replace ASCIIHexDecode with RunLengthDecode
 | 
			
		||||
            # (packbits) or LZWDecode (tiff/lzw compression).  Note that
 | 
			
		||||
            # PDF 1.2 also supports Flatedecode (zip compression).
 | 
			
		||||
 | 
			
		||||
            params = None
 | 
			
		||||
            decode = None
 | 
			
		||||
 | 
			
		||||
            #
 | 
			
		||||
            # Get image characteristics
 | 
			
		||||
 | 
			
		||||
            width, height = im.size
 | 
			
		||||
 | 
			
		||||
            dict_obj = {"BitsPerComponent": 8}
 | 
			
		||||
            if im.mode == "1":
 | 
			
		||||
                if features.check("libtiff"):
 | 
			
		||||
                    filter = "CCITTFaxDecode"
 | 
			
		||||
                    dict_obj["BitsPerComponent"] = 1
 | 
			
		||||
                    params = PdfParser.PdfArray(
 | 
			
		||||
                        [
 | 
			
		||||
                            PdfParser.PdfDict(
 | 
			
		||||
                                {
 | 
			
		||||
                                    "K": -1,
 | 
			
		||||
                                    "BlackIs1": True,
 | 
			
		||||
                                    "Columns": width,
 | 
			
		||||
                                    "Rows": height,
 | 
			
		||||
                                }
 | 
			
		||||
                            )
 | 
			
		||||
                        ]
 | 
			
		||||
                    )
 | 
			
		||||
                else:
 | 
			
		||||
                    filter = "DCTDecode"
 | 
			
		||||
                dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray")
 | 
			
		||||
                procset = "ImageB"  # grayscale
 | 
			
		||||
            elif im.mode == "L":
 | 
			
		||||
                filter = "DCTDecode"
 | 
			
		||||
                # params = f"<< /Predictor 15 /Columns {width-2} >>"
 | 
			
		||||
                dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray")
 | 
			
		||||
                procset = "ImageB"  # grayscale
 | 
			
		||||
            elif im.mode == "LA":
 | 
			
		||||
                filter = "JPXDecode"
 | 
			
		||||
                # params = f"<< /Predictor 15 /Columns {width-2} >>"
 | 
			
		||||
                procset = "ImageB"  # grayscale
 | 
			
		||||
                dict_obj["SMaskInData"] = 1
 | 
			
		||||
            elif im.mode == "P":
 | 
			
		||||
                filter = "ASCIIHexDecode"
 | 
			
		||||
                palette = im.getpalette()
 | 
			
		||||
                dict_obj["ColorSpace"] = [
 | 
			
		||||
                    PdfParser.PdfName("Indexed"),
 | 
			
		||||
                    PdfParser.PdfName("DeviceRGB"),
 | 
			
		||||
                    255,
 | 
			
		||||
                    PdfParser.PdfBinary(palette),
 | 
			
		||||
                ]
 | 
			
		||||
                procset = "ImageI"  # indexed color
 | 
			
		||||
            elif im.mode == "RGB":
 | 
			
		||||
                filter = "DCTDecode"
 | 
			
		||||
                dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB")
 | 
			
		||||
                procset = "ImageC"  # color images
 | 
			
		||||
            elif im.mode == "RGBA":
 | 
			
		||||
                filter = "JPXDecode"
 | 
			
		||||
                procset = "ImageC"  # color images
 | 
			
		||||
                dict_obj["SMaskInData"] = 1
 | 
			
		||||
            elif im.mode == "CMYK":
 | 
			
		||||
                filter = "DCTDecode"
 | 
			
		||||
                dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceCMYK")
 | 
			
		||||
                procset = "ImageC"  # color images
 | 
			
		||||
                decode = [1, 0, 1, 0, 1, 0, 1, 0]
 | 
			
		||||
            else:
 | 
			
		||||
                msg = f"cannot save mode {im.mode}"
 | 
			
		||||
                raise ValueError(msg)
 | 
			
		||||
 | 
			
		||||
            #
 | 
			
		||||
            # image
 | 
			
		||||
 | 
			
		||||
            op = io.BytesIO()
 | 
			
		||||
 | 
			
		||||
            if filter == "ASCIIHexDecode":
 | 
			
		||||
                ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
            elif filter == "CCITTFaxDecode":
 | 
			
		||||
                im.save(
 | 
			
		||||
                    op,
 | 
			
		||||
                    "TIFF",
 | 
			
		||||
                    compression="group4",
 | 
			
		||||
                    # use a single strip
 | 
			
		||||
                    strip_size=math.ceil(im.width / 8) * im.height,
 | 
			
		||||
                )
 | 
			
		||||
            elif filter == "DCTDecode":
 | 
			
		||||
                Image.SAVE["JPEG"](im, op, filename)
 | 
			
		||||
            elif filter == "JPXDecode":
 | 
			
		||||
                del dict_obj["BitsPerComponent"]
 | 
			
		||||
                Image.SAVE["JPEG2000"](im, op, filename)
 | 
			
		||||
            elif filter == "FlateDecode":
 | 
			
		||||
                ImageFile._save(im, op, [("zip", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
            elif filter == "RunLengthDecode":
 | 
			
		||||
                ImageFile._save(im, op, [("packbits", (0, 0) + im.size, 0, im.mode)])
 | 
			
		||||
            else:
 | 
			
		||||
                msg = f"unsupported PDF filter ({filter})"
 | 
			
		||||
                raise ValueError(msg)
 | 
			
		||||
 | 
			
		||||
            stream = op.getvalue()
 | 
			
		||||
            if filter == "CCITTFaxDecode":
 | 
			
		||||
                stream = stream[8:]
 | 
			
		||||
                filter = PdfParser.PdfArray([PdfParser.PdfName(filter)])
 | 
			
		||||
            else:
 | 
			
		||||
                filter = PdfParser.PdfName(filter)
 | 
			
		||||
 | 
			
		||||
            existing_pdf.write_obj(
 | 
			
		||||
                image_refs[page_number],
 | 
			
		||||
                stream=stream,
 | 
			
		||||
                Type=PdfParser.PdfName("XObject"),
 | 
			
		||||
                Subtype=PdfParser.PdfName("Image"),
 | 
			
		||||
                Width=width,  # * 72.0 / x_resolution,
 | 
			
		||||
                Height=height,  # * 72.0 / y_resolution,
 | 
			
		||||
                Filter=filter,
 | 
			
		||||
                Decode=decode,
 | 
			
		||||
                DecodeParms=params,
 | 
			
		||||
                **dict_obj,
 | 
			
		||||
            )
 | 
			
		||||
            image_ref, procset = _write_image(im, filename, existing_pdf, image_refs)
 | 
			
		||||
 | 
			
		||||
            #
 | 
			
		||||
            # page
 | 
			
		||||
| 
						 | 
				
			
			@ -246,13 +259,13 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
                page_refs[page_number],
 | 
			
		||||
                Resources=PdfParser.PdfDict(
 | 
			
		||||
                    ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)],
 | 
			
		||||
                    XObject=PdfParser.PdfDict(image=image_refs[page_number]),
 | 
			
		||||
                    XObject=PdfParser.PdfDict(image=image_ref),
 | 
			
		||||
                ),
 | 
			
		||||
                MediaBox=[
 | 
			
		||||
                    0,
 | 
			
		||||
                    0,
 | 
			
		||||
                    width * 72.0 / x_resolution,
 | 
			
		||||
                    height * 72.0 / y_resolution,
 | 
			
		||||
                    im.width * 72.0 / x_resolution,
 | 
			
		||||
                    im.height * 72.0 / y_resolution,
 | 
			
		||||
                ],
 | 
			
		||||
                Contents=contents_refs[page_number],
 | 
			
		||||
            )
 | 
			
		||||
| 
						 | 
				
			
			@ -261,8 +274,8 @@ def _save(im, fp, filename, save_all=False):
 | 
			
		|||
            # page contents
 | 
			
		||||
 | 
			
		||||
            page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % (
 | 
			
		||||
                width * 72.0 / x_resolution,
 | 
			
		||||
                height * 72.0 / y_resolution,
 | 
			
		||||
                im.width * 72.0 / x_resolution,
 | 
			
		||||
                im.height * 72.0 / y_resolution,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            existing_pdf.write_obj(contents_refs[page_number], stream=page_contents)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,6 +74,9 @@ class WebPImageFile(ImageFile.ImageFile):
 | 
			
		|||
        self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
 | 
			
		||||
        self.n_frames = frame_count
 | 
			
		||||
        self.is_animated = self.n_frames > 1
 | 
			
		||||
        ret = self._decoder.get_next()
 | 
			
		||||
        if ret is not None:
 | 
			
		||||
            self.info["duration"] = ret[1]
 | 
			
		||||
        self._mode = "RGB" if mode == "RGBX" else mode
 | 
			
		||||
        self.rawmode = mode
 | 
			
		||||
        self.tile = []
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +93,7 @@ class WebPImageFile(ImageFile.ImageFile):
 | 
			
		|||
            self.info["xmp"] = xmp
 | 
			
		||||
 | 
			
		||||
        # Initialize seek state
 | 
			
		||||
        self._reset(reset=False)
 | 
			
		||||
        self._reset()
 | 
			
		||||
 | 
			
		||||
    def _getexif(self):
 | 
			
		||||
        if "exif" not in self.info:
 | 
			
		||||
| 
						 | 
				
			
			@ -113,8 +116,7 @@ class WebPImageFile(ImageFile.ImageFile):
 | 
			
		|||
        # Set logical frame to requested position
 | 
			
		||||
        self.__logical_frame = frame
 | 
			
		||||
 | 
			
		||||
    def _reset(self, reset=True):
 | 
			
		||||
        if reset:
 | 
			
		||||
    def _reset(self):
 | 
			
		||||
        self._decoder.reset()
 | 
			
		||||
        self.__physical_frame = 0
 | 
			
		||||
        self.__loaded = -1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -130,9 +130,9 @@ deps = {
 | 
			
		|||
        "bins": ["cjpeg.exe", "djpeg.exe"],
 | 
			
		||||
    },
 | 
			
		||||
    "zlib": {
 | 
			
		||||
        "url": "https://zlib.net/zlib1213.zip",
 | 
			
		||||
        "filename": "zlib1213.zip",
 | 
			
		||||
        "dir": "zlib-1.2.13",
 | 
			
		||||
        "url": "https://zlib.net/zlib13.zip",
 | 
			
		||||
        "filename": "zlib13.zip",
 | 
			
		||||
        "dir": "zlib-1.3",
 | 
			
		||||
        "license": "README",
 | 
			
		||||
        "license_pattern": "Copyright notice:\n\n(.+)$",
 | 
			
		||||
        "build": [
 | 
			
		||||
| 
						 | 
				
			
			@ -335,9 +335,9 @@ deps = {
 | 
			
		|||
        "libs": [r"imagequant.lib"],
 | 
			
		||||
    },
 | 
			
		||||
    "harfbuzz": {
 | 
			
		||||
        "url": "https://github.com/harfbuzz/harfbuzz/archive/8.0.0.zip",
 | 
			
		||||
        "filename": "harfbuzz-8.0.0.zip",
 | 
			
		||||
        "dir": "harfbuzz-8.0.0",
 | 
			
		||||
        "url": "https://github.com/harfbuzz/harfbuzz/archive/8.1.1.zip",
 | 
			
		||||
        "filename": "harfbuzz-8.1.1.zip",
 | 
			
		||||
        "dir": "harfbuzz-8.1.1",
 | 
			
		||||
        "license": "COPYING",
 | 
			
		||||
        "build": [
 | 
			
		||||
            *cmds_cmake(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user