From 30a52cb0ed07e5b6bb71bb60c5a0e8d2f6f95188 Mon Sep 17 00:00:00 2001 From: Jeremy Singer-Vine Date: Sat, 29 Jul 2023 14:30:00 -0400 Subject: [PATCH] Add PDF.path & fix .to_image() for zipped files These are related changes, since the previous approach of using `PDF.stream.name` to distinguish between on-path and filelike-object PDFs doesn't work for zipped files (and possibly other filelike-objects that have a `name` property). --- pdfplumber/display.py | 13 +++++++++---- pdfplumber/pdf.py | 11 ++++++++++- tests/pdfs/issue-948.zip | Bin 0 -> 12480 bytes tests/test_display.py | 11 +++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 tests/pdfs/issue-948.zip diff --git a/pdfplumber/display.py b/pdfplumber/display.py index 6a916335..f8caa5c0 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 @@ class COLORS: 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 @@ def __init__( 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 @@ def reset(self) -> "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 ec801987..e98090d8 100644 --- a/pdfplumber/pdf.py +++ b/pdfplumber/pdf.py @@ -28,6 +28,7 @@ def __init__( 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 @@ def __init__( ): 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 @@ def open( 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 0000000000000000000000000000000000000000..de7efc21a30f75ea62f3c2adfb41955ee1d4efe9 GIT binary patch literal 12480 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 Dm!OU2 literal 0 HcmV?d00001 diff --git a/tests/test_display.py b/tests/test_display.py index 88bc0cd5..faf3d162 100644 --- a/tests/test_display.py +++ b/tests/test_display.py @@ -3,6 +3,7 @@ import logging import os import unittest +from zipfile import ZipFile import PIL.Image import pytest @@ -112,3 +113,13 @@ def test_password(self): 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()