Skip to content

Commit

Permalink
Merge pull request #6470 from radarhere/pdf_ccittfaxdecode
Browse files Browse the repository at this point in the history
Save 1 mode PDF using CCITTFaxDecode filter
  • Loading branch information
hugovk committed Aug 7, 2022
2 parents 38e411d + 2b14d83 commit ae14255
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 14 deletions.
12 changes: 8 additions & 4 deletions Tests/test_file_libtiff.py
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_pdf.py
Expand Up @@ -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):
Expand Down
45 changes: 37 additions & 8 deletions src/PIL/PdfImagePlugin.py
Expand Up @@ -21,6 +21,7 @@
##

import io
import math
import os
import time

Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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":
Expand All @@ -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,
)

Expand Down
3 changes: 2 additions & 1 deletion src/PIL/TiffImagePlugin.py
Expand Up @@ -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])
Expand Down

0 comments on commit ae14255

Please sign in to comment.