Skip to content

Commit

Permalink
BUG: Fix test_watermarking_reportlab_rendering() (#2203)
Browse files Browse the repository at this point in the history
This fixes the issue spotted in #2191

The solution was to re-introduce calls to `PageObject._push_pop_gs()`,
in `PageObject._merge_page` & `PageObject._merge_page_writer()`,
but to optimize `PageObject._push_pop_gs()` by introducing a `ContentsStream.isolate_graphics_state()` method.
  • Loading branch information
Lucas-C authored Sep 24, 2023
1 parent ada7675 commit 91b6dcd
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 28 deletions.
26 changes: 5 additions & 21 deletions pypdf/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,23 +880,6 @@ def _content_stream_rename(
raise KeyError(f"type of operands is {type(operands)}")
return stream

@staticmethod
def _push_pop_gs(
contents: Any,
pdf: Union[None, PdfReaderProtocol, PdfWriterProtocol],
use_original: bool = True,
) -> ContentStream:
# adds a graphics state "push" and "pop" to the beginning and end
# of a content stream. This isolates it from changes such as
# transformation matricies.
if use_original:
stream = contents
else:
stream = ContentStream(contents, pdf)
stream.operations.insert(0, ([], "q"))
stream.operations.append(([], "Q"))
return stream

@staticmethod
def _add_transformation_matrix(
contents: Any,
Expand Down Expand Up @@ -1127,6 +1110,7 @@ def _merge_page(
new_content_array = ArrayObject()
original_content = self.get_contents()
if original_content is not None:
original_content.isolate_graphics_state()
new_content_array.append(original_content)

page2content = page2.get_contents()
Expand Down Expand Up @@ -1154,7 +1138,7 @@ def _merge_page(
page2content = PageObject._content_stream_rename(
page2content, rename, self.pdf
)
page2content = PageObject._push_pop_gs(page2content, self.pdf)
page2content.isolate_graphics_state()
if over:
new_content_array.append(page2content)
else:
Expand Down Expand Up @@ -1262,9 +1246,9 @@ def _merge_page_writer(
pass

new_content_array = ArrayObject()

original_content = self.get_contents()
if original_content is not None:
original_content.isolate_graphics_state()
new_content_array.append(original_content)

page2content = page2.get_contents()
Expand Down Expand Up @@ -1292,7 +1276,7 @@ def _merge_page_writer(
page2content = PageObject._content_stream_rename(
page2content, rename, self.pdf
)
page2content = PageObject._push_pop_gs(page2content, self.pdf)
page2content.isolate_graphics_state()
if over:
new_content_array.append(page2content)
else:
Expand Down Expand Up @@ -1642,7 +1626,7 @@ def add_transformation(
content = self.get_contents()
if content is not None:
content = PageObject._add_transformation_matrix(content, self.pdf, ctm)
content = PageObject._push_pop_gs(content, self.pdf)
content.isolate_graphics_state()
self.replace_contents(content)
# if expanding the page to fit a new page, calculate the new media box size
if expand:
Expand Down
4 changes: 2 additions & 2 deletions pypdf/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,11 @@ def mark_location(stream: StreamType) -> None:


def b_(s: Union[str, bytes]) -> bytes:
if isinstance(s, bytes):
return s
bc = B_CACHE
if s in bc:
return bc[s]
if isinstance(s, bytes):
return s
try:
r = s.encode("latin-1")
if len(s) < 2:
Expand Down
7 changes: 7 additions & 0 deletions pypdf/generic/_data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,13 @@ def operations(self, operations: List[Tuple[Any, Any]]) -> None:
self._operations = operations
self._data = b""

def isolate_graphics_state(self) -> None:
if self._operations:
self._operations.insert(0, ([], "q"))
self._operations.append(([], "Q"))
elif self._data:
self._data = b"q\n" + b_(self._data) + b"Q\n"

# This overrides the parent method:
def write_to_stream(
self, stream: StreamType, encryption_key: Union[None, str, bytes] = None
Expand Down
6 changes: 1 addition & 5 deletions tests/test_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1619,10 +1619,9 @@ def test_watermark_rendering(tmp_path):
assert image_similarity(png_path, target_png_path) >= 0.95


@pytest.mark.xfail(reason="issue introduced with pypdf==3.15.4")
def test_watermarking_reportlab_rendering(tmp_path):
"""
This test shows that the merged page is rotated+mirrored.
This test is showing a rotated+mirrored watermark in pypdf==3.15.4.
Replacing the generate_base with e.g. the crazyones did not show the issue.
"""
Expand All @@ -1637,9 +1636,6 @@ def test_watermarking_reportlab_rendering(tmp_path):
base_page.merge_page(watermark)
writer.add_page(base_page)

for page in writer.pages:
page.compress_content_streams()

target_png_path = RESOURCE_ROOT / "test_watermarking_reportlab_rendering.png"
pdf_path = tmp_path / "out.pdf"
png_path = tmp_path / "test_watermarking_reportlab_rendering.png"
Expand Down

0 comments on commit 91b6dcd

Please sign in to comment.