diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index a43548ae0f3..3084425a406 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1011,14 +1011,18 @@ def test_save_multistrip(self, compression, tmp_path): # Assert that there are multiple strips assert len(im.tag_v2[STRIPOFFSETS]) > 1 - def test_save_single_strip(self, tmp_path): + @pytest.mark.parametrize("argument", (True, False)) + def test_save_single_strip(self, argument, tmp_path): im = hopper("RGB").resize((256, 256)) out = str(tmp_path / "temp.tif") - TiffImagePlugin.STRIP_SIZE = 2**18 + if not argument: + TiffImagePlugin.STRIP_SIZE = 2**18 try: - - im.save(out, compression="tiff_adobe_deflate") + arguments = {"compression": "tiff_adobe_deflate"} + if argument: + arguments["strip_size"] = 2**18 + im.save(out, **arguments) with Image.open(out) as im: assert len(im.tag_v2[STRIPOFFSETS]) == 1 diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index c71d4f5f22b..310619fb255 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -43,7 +43,7 @@ def test_monochrome(tmp_path): # Act / Assert outfile = helper_save_as_pdf(tmp_path, mode) - assert os.path.getsize(outfile) < 15000 + assert os.path.getsize(outfile) < 5000 def test_greyscale(tmp_path): diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 2109a6f52cb..181a05b8d26 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -21,6 +21,7 @@ ## import io +import math import os import time @@ -123,8 +124,26 @@ def _save(im, fp, filename, save_all=False): params = None decode = None + # + # Get image characteristics + + width, height = im.size + if im.mode == "1": - filter = "DCTDecode" + filter = "CCITTFaxDecode" + bits = 1 + params = PdfParser.PdfArray( + [ + PdfParser.PdfDict( + { + "K": -1, + "BlackIs1": True, + "Columns": width, + "Rows": height, + } + ) + ] + ) colorspace = PdfParser.PdfName("DeviceGray") procset = "ImageB" # grayscale elif im.mode == "L": @@ -161,6 +180,14 @@ def _save(im, fp, filename, save_all=False): 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 == "FlateDecode": @@ -170,22 +197,24 @@ def _save(im, fp, filename, save_all=False): else: raise ValueError(f"unsupported PDF filter ({filter})") - # - # Get image characteristics - - width, height = im.size + 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=op.getvalue(), + stream=stream, Type=PdfParser.PdfName("XObject"), Subtype=PdfParser.PdfName("Image"), Width=width, # * 72.0 / resolution, Height=height, # * 72.0 / resolution, - Filter=PdfParser.PdfName(filter), + Filter=filter, BitsPerComponent=bits, Decode=decode, - DecodeParams=params, + DecodeParms=params, ColorSpace=colorspace, ) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 0dd49340d4b..da33cc5a501 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1684,7 +1684,8 @@ def _save(im, fp, filename): stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) # aim for given strip size (64 KB by default) when using libtiff writer if libtiff: - rows_per_strip = 1 if stride == 0 else min(STRIP_SIZE // stride, im.size[1]) + im_strip_size = encoderinfo.get("strip_size", STRIP_SIZE) + rows_per_strip = 1 if stride == 0 else min(im_strip_size // stride, im.size[1]) # JPEG encoder expects multiple of 8 rows if compression == "jpeg": rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, im.size[1])