Merge pull request #6961 from jvanderneutstulen/add-save-pdf-dpi-tuple

Support saving PDF with different X and Y resolution
This commit is contained in:
Andrew Murray 2023-02-24 12:50:42 +11:00 committed by GitHub
commit 4777379465
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 7 deletions

View File

@ -80,6 +80,34 @@ def test_resolution(tmp_path):
assert size == (61.44, 61.44) assert size == (61.44, 61.44)
@pytest.mark.parametrize(
"params",
(
{"dpi": (75, 150)},
{"dpi": (75, 150), "resolution": 200},
),
)
def test_dpi(params, tmp_path):
im = hopper()
outfile = str(tmp_path / "temp.pdf")
im.save(outfile, **params)
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( @mark_if_feature_version(
pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing" 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 image, will determine the physical dimensions of the page that will be
saved in the PDF. 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** **title**
The documents title. If not appending to an existing PDF file, this will The documents title. If not appending to an existing PDF file, this will
default to the filename. default to the filename.

View File

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