Skip to content

Commit e13a1e0

Browse files
Nid01stefan6419846
andauthored
BUG: PageObject.scale() scales media box incorrectly (#3489)
Closes #3487. --------- Co-authored-by: Stefan <[email protected]>
1 parent 0859b35 commit e13a1e0

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

pypdf/_page.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,7 @@ def scale(self, sx: float, sy: float) -> None:
15031503
Scale a page by the given factors by applying a transformation matrix
15041504
to its content and updating the page size.
15051505
1506-
This updates the various page boundaries (mediabox, cropbox, etc.)
1506+
This updates the various page boundaries (bleedbox, trimbox, etc.)
15071507
and the contents of the page.
15081508
15091509
Args:
@@ -1512,11 +1512,11 @@ def scale(self, sx: float, sy: float) -> None:
15121512
15131513
"""
15141514
self.add_transformation((sx, 0, 0, sy, 0, 0))
1515-
self.mediabox = self.mediabox.scale(sx, sy)
1516-
self.cropbox = self.cropbox.scale(sx, sy)
15171515
self.bleedbox = self.bleedbox.scale(sx, sy)
15181516
self.trimbox = self.trimbox.scale(sx, sy)
15191517
self.artbox = self.artbox.scale(sx, sy)
1518+
self.cropbox = self.cropbox.scale(sx, sy)
1519+
self.mediabox = self.mediabox.scale(sx, sy)
15201520

15211521
if PG.ANNOTS in self:
15221522
annotations = self[PG.ANNOTS]

tests/test_page.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import json
33
import logging
44
import math
5+
import shutil
6+
import subprocess
57
from copy import deepcopy
68
from io import BytesIO
79
from pathlib import Path
@@ -28,11 +30,13 @@
2830
)
2931

3032
from . import get_data_from_url, normalize_warnings
33+
from .test_images import image_similarity
3134

3235
TESTS_ROOT = Path(__file__).parent.resolve()
3336
PROJECT_ROOT = TESTS_ROOT.parent
3437
RESOURCE_ROOT = PROJECT_ROOT / "resources"
3538
SAMPLE_ROOT = PROJECT_ROOT / "sample-files"
39+
GHOSTSCRIPT_BINARY = shutil.which("gs")
3640

3741

3842
def get_all_sample_files():
@@ -1504,3 +1508,67 @@ def __getitem__(self, item) -> Any:
15041508
page[NameObject("/Resources")] = resources
15051509
with mock.patch.object(none_reference, "get_object", return_value=None):
15061510
assert page.extract_text() == ""
1511+
1512+
1513+
@pytest.mark.enable_socket
1514+
def test_scale_by():
1515+
"""Tests for #3487"""
1516+
url = "https://github.com/user-attachments/files/22685841/input.pdf"
1517+
name = "issue3487.pdf"
1518+
reader = PdfReader(BytesIO(get_data_from_url(url, name=name)))
1519+
1520+
original_box = RectangleObject((0, 0, 595.275604, 841.88974))
1521+
expected_box = RectangleObject((0.0, 0.0, 297.637802, 420.94487))
1522+
for page in reader.pages:
1523+
assert page.artbox == original_box
1524+
assert page.bleedbox == original_box
1525+
assert page.cropbox == original_box
1526+
assert page.mediabox == original_box
1527+
assert page.trimbox == original_box
1528+
1529+
page.scale_by(0.5)
1530+
assert page.artbox == expected_box
1531+
assert page.bleedbox == expected_box
1532+
assert page.cropbox == expected_box
1533+
assert page.mediabox == expected_box
1534+
assert page.trimbox == expected_box
1535+
1536+
1537+
@pytest.mark.enable_socket
1538+
@pytest.mark.skipif(GHOSTSCRIPT_BINARY is None, reason="Requires Ghostscript")
1539+
def test_box_rendering(tmp_path):
1540+
"""Tests for issue #3487."""
1541+
url = "https://github.com/user-attachments/files/22685841/input.pdf"
1542+
name = "issue3487.pdf"
1543+
reader = PdfReader(BytesIO(get_data_from_url(url, name=name)))
1544+
writer = PdfWriter()
1545+
1546+
for page in reader.pages:
1547+
page.scale_by(0.5)
1548+
writer.add_page(page)
1549+
1550+
target_png_path = tmp_path / "target.png"
1551+
url = "https://github.com/user-attachments/assets/e9c2271c-bfc3-4a6f-8c91-ffefa24502e2"
1552+
name = "issue3487.png"
1553+
target_png_path.write_bytes(get_data_from_url(url, name=name))
1554+
1555+
pdf_path = tmp_path / "out.pdf"
1556+
writer.write(pdf_path)
1557+
1558+
for box in ["Art", "Bleed", "Crop", "Media", "Trim"]:
1559+
png_path = tmp_path / f"{box}.png"
1560+
# False positive: https://github.com/PyCQA/bandit/issues/333
1561+
subprocess.run( # noqa: S603
1562+
[
1563+
GHOSTSCRIPT_BINARY,
1564+
f"-dUse{box}Box",
1565+
"-dFirstPage=1",
1566+
"-dLastPage=1",
1567+
"-sDEVICE=pngalpha",
1568+
"-o",
1569+
png_path,
1570+
pdf_path,
1571+
]
1572+
)
1573+
assert png_path.is_file(), box
1574+
assert image_similarity(png_path, target_png_path) >= 0.95, box

0 commit comments

Comments
 (0)