diff --git a/pdfplumber/display.py b/pdfplumber/display.py index 6a91633..f8caa5c 100644 --- a/pdfplumber/display.py +++ b/pdfplumber/display.py @@ -1,5 +1,5 @@ +import pathlib from io import BufferedReader, BytesIO -from pathlib import Path from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union import PIL.Image @@ -35,14 +35,18 @@ T_contains_points = Union[Tuple[T_point, ...], List[T_point], T_obj] def get_page_image( stream: Union[BufferedReader, BytesIO], + path: Optional[pathlib.Path], page_ix: int, resolution: Union[int, float], password: Optional[str], antialias: bool = False, ) -> PIL.Image.Image: + + src: Union[pathlib.Path, BufferedReader, BytesIO] + # If we are working with a file object saved to disk - if hasattr(stream, "name"): - src = stream.name + if path: + src = path # If we instead are working with a BytesIO stream else: @@ -79,6 +83,7 @@ class PageImage: if original is None: self.original = get_page_image( stream=page.pdf.stream, + path=page.pdf.path, page_ix=page.page_number - 1, resolution=resolution, antialias=antialias, @@ -133,7 +138,7 @@ class PageImage: def save( self, - dest: Union[str, Path, BytesIO], + dest: Union[str, pathlib.Path, BytesIO], format: str = "PNG", quantize: bool = True, colors: int = 256, diff --git a/pdfplumber/pdf.py b/pdfplumber/pdf.py index ec80198..e98090d 100644 --- a/pdfplumber/pdf.py +++ b/pdfplumber/pdf.py @@ -28,6 +28,7 @@ class PDF(Container): self, stream: Union[BufferedReader, BytesIO], stream_is_external: bool = False, + path: Optional[pathlib.Path] = None, pages: Optional[Union[List[int], Tuple[int]]] = None, laparams: Optional[Dict[str, Any]] = None, password: Optional[str] = None, @@ -35,6 +36,7 @@ class PDF(Container): ): self.stream = stream self.stream_is_external = stream_is_external + self.path = path self.pages_to_parse = pages self.laparams = None if laparams is None else LAParams(**laparams) self.password = password @@ -70,20 +72,27 @@ class PDF(Container): repair: bool = False, ) -> "PDF": - stream: Union[str, pathlib.Path, BufferedReader, BytesIO] + stream: Union[BufferedReader, BytesIO] + if repair: stream = _repair(path_or_fp, password=password) stream_is_external = False + # Although the original file has a path, + # the repaired version does not + path = None elif isinstance(path_or_fp, (str, pathlib.Path)): stream = open(path_or_fp, "rb") stream_is_external = False + path = pathlib.Path(path_or_fp) else: stream = path_or_fp stream_is_external = True + path = None try: return cls( stream, + path=path, pages=pages, laparams=laparams, password=password, diff --git a/tests/pdfs/issue-948.zip b/tests/pdfs/issue-948.zip new file mode 100644 index 0000000..de7efc2 Binary files /dev/null and b/tests/pdfs/issue-948.zip differ diff --git a/tests/test_display.py b/tests/test_display.py index 88bc0cd..faf3d16 100644 --- a/tests/test_display.py +++ b/tests/test_display.py @@ -3,6 +3,7 @@ import io import logging import os import unittest +from zipfile import ZipFile import PIL.Image import pytest @@ -112,3 +113,13 @@ class Test(unittest.TestCase): path = os.path.join(HERE, "pdfs/password-example.pdf") with pdfplumber.open(path, password="test") as pdf: pdf.pages[0].to_image() + + def test_zip(self): + # See https://github.com/jsvine/pdfplumber/issues/948 + # reproducer.py + path = os.path.join(HERE, "pdfs/issue-948.zip") + with ZipFile(path) as zip_file: + with zip_file.open("dummy.pdf") as pdf_file: + with pdfplumber.open(pdf_file) as pdf: + page = pdf.pages[0] + page.to_image()