Support saving PDF with different X and Y resolution.

Add a `dpi` parameter to the PDF save function, which accepts
a tuple with X and Y dpi.
This is useful for converting tiffg3 (fax) images to pdf,
which have split dpi like (204,391), (204,196) or (204,98).
This commit is contained in:
Jasper van der Neut 2023-02-21 10:34:41 +01:00
parent d48dca3dc4
commit 36bcc0a898
3 changed files with 59 additions and 7 deletions

View File

@ -80,6 +80,48 @@ def test_resolution(tmp_path):
assert size == (61.44, 61.44)
def test_dpi(tmp_path):
im = hopper()
outfile = str(tmp_path / "temp.pdf")
im.save(outfile, dpi=(75, 150))
with open(outfile, "rb") as fp:
contents = fp.read()
size = tuple(
float(d)
for d in contents.split(b"stream\nq ")[1].split(b" 0 0 cm")[0].split(b" 0 0 ")
)
assert size == (122.88, 61.44)
size = tuple(
float(d) for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
)
assert size == (122.88, 61.44)
def test_resolution_and_dpi(tmp_path):
im = hopper()
outfile = str(tmp_path / "temp.pdf")
im.save(outfile, resolution=200, dpi=(75, 150))
with open(outfile, "rb") as fp:
contents = fp.read()
size = tuple(
float(d)
for d in contents.split(b"stream\nq ")[1].split(b" 0 0 cm")[0].split(b" 0 0 ")
)
assert size == (122.88, 61.44)
size = tuple(
float(d) for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split()
)
assert size == (122.88, 61.44)
@mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing"
)

View File

@ -1497,6 +1497,11 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
image, will determine the physical dimensions of the page that will be
saved in the PDF.
**dpi**
A tuple of (x_resolution, y_resolution), with inches as the resolution
unit. If both the ``resolution`` parameter and the ``dpi`` parameter are
present, ``resolution`` will be ignored.
**title**
The documents title. If not appending to an existing PDF file, this will
default to the filename.

View File

@ -53,7 +53,12 @@ def _save(im, fp, filename, save_all=False):
else:
existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="w+b")
resolution = im.encoderinfo.get("resolution", 72.0)
x_resolution = y_resolution = im.encoderinfo.get("resolution", 72.0)
dpi = im.encoderinfo.get("dpi")
if dpi:
x_resolution = dpi[0]
y_resolution = dpi[1]
info = {
"title": None
@ -214,8 +219,8 @@ def _save(im, fp, filename, save_all=False):
stream=stream,
Type=PdfParser.PdfName("XObject"),
Subtype=PdfParser.PdfName("Image"),
Width=width, # * 72.0 / resolution,
Height=height, # * 72.0 / resolution,
Width=width, # * 72.0 / x_resolution,
Height=height, # * 72.0 / y_resolution,
Filter=filter,
BitsPerComponent=bits,
Decode=decode,
@ -235,8 +240,8 @@ def _save(im, fp, filename, save_all=False):
MediaBox=[
0,
0,
width * 72.0 / resolution,
height * 72.0 / resolution,
width * 72.0 / x_resolution,
height * 72.0 / y_resolution,
],
Contents=contents_refs[page_number],
)
@ -245,8 +250,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 / resolution,
height * 72.0 / resolution,
width * 72.0 / x_resolution,
height * 72.0 / y_resolution,
)
existing_pdf.write_obj(contents_refs[page_number], stream=page_contents)