Repository: py-pdf/pypdf
Issue category: Performance Issue

Performance Bug: Reading large compressed images takes huge time to process
Reading a compressed image takes (>10minutes), if the image is large-ish (>250kb)

## Environment

Which environment were you using when you encountered the problem?

```bash
$ python -m platform
Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.35

$ python -c "import pypdf;print(pypdf._debug_versions)"
pypdf==4.2.0, crypt_provider=('pycryptodome', '3.19.0'), PIL=9.5.0
```

## Code + PDF

This is a minimal, complete example that shows the issue:

```python
import sys
from datetime import datetime

from pypdf import PdfReader

def log(msg):
    print(f"[{datetime.now()}] {msg}\n")

file = sys.argv[1]

log("Reading File PyPDF..")
images = []
pypdf_reader = PdfReader(file)
for pidx, page in enumerate(pypdf_reader.pages, start=1):
    log(f"Reading page {pidx}")
    for iidx, image in enumerate(page.images, start=1):
        log(f"Processing Image {iidx}")
        images.append(image.image)
    log(f"Competed page {pidx}")
log("Completed Reading File PyPDF!")
```

Attached is a sample PDF I created that can reproduce this error. 
[file_with_image.pdf](https://github.com/py-pdf/pypdf/files/15305860/file_with_image.pdf)


The PDF can be added to the tests as well.


## Output

This is the complete output I see:
Look at the time difference between Image 6 and Image 7. It is close to 12 minutes

```
[2024-05-14 14:06:52.927018] Reading File PyPDF..
[2024-05-14 14:06:52.929346] Reading page 1
[2024-05-14 14:06:52.971993] Processing Image 1
[2024-05-14 14:06:52.993297] Processing Image 2
[2024-05-14 14:06:53.007304] Processing Image 3
[2024-05-14 14:06:53.021166] Processing Image 4
[2024-05-14 14:06:53.049529] Processing Image 5
[2024-05-14 14:06:53.051842] Processing Image 6

[2024-05-14 14:18:46.906472] Processing Image 7
[2024-05-14 14:18:47.088749] Processing Image 8
[2024-05-14 14:18:47.092159] Processing Image 9
[2024-05-14 14:18:47.099422] Processing Image 10
[2024-05-14 14:18:47.099513] Competed page 1
```



Functions to edit:
pypdf/filters.py:decompress

Patch:
diff --git a/pypdf/filters.py b/pypdf/filters.py
index d62cf7842..d573ae30e 100644
--- a/pypdf/filters.py
+++ b/pypdf/filters.py
@@ -80,14 +80,19 @@ def decompress(data: bytes) -> bytes:
     try:
         return zlib.decompress(data)
     except zlib.error:
-        d = zlib.decompressobj(zlib.MAX_WBITS | 32)
-        result_str = b""
-        for b in [data[i : i + 1] for i in range(len(data))]:
-            try:
-                result_str += d.decompress(b)
-            except zlib.error:
-                pass
-        return result_str
+        try:
+            # For larger files, use Decompress object to enable buffered reading
+            return zlib.decompressobj().decompress(data)
+        except zlib.error:
+            # If still failed, then try with increased window size
+            d = zlib.decompressobj(zlib.MAX_WBITS | 32)
+            result_str = b""
+            for b in [data[i : i + 1] for i in range(len(data))]:
+                try:
+                    result_str += d.decompress(b)
+                except zlib.error:
+                    pass
+            return result_str
 
 
 class FlateDecode:

Test patch: diff --git a/tests/bench.py b/tests/bench.py
index dcfc30a9b..ea5597f88 100644
--- a/tests/bench.py
+++ b/tests/bench.py
@@ -227,3 +227,15 @@ def test_image_new_property_performance(benchmark):
     data = BytesIO(get_data_from_url(url, name=name))
 
     benchmark(image_new_property, data)
+
+
+def image_extraction(data):
+    reader = PdfReader(data)
+    list(reader.pages[0].images)
+
+
+@pytest.mark.enable_socket()
+def test_large_compressed_image_performance(benchmark):
+    url = "https://github.com/py-pdf/pypdf/files/15306199/file_with_large_compressed_image.pdf"
+    data = BytesIO(get_data_from_url(url, name="file_with_large_compressed_image.pdf"))
+    benchmark(image_extraction, data)
diff --git a/tests/test_images.py b/tests/test_images.py
index ad694d669..e77090171 100644
--- a/tests/test_images.py
+++ b/tests/test_images.py
@@ -346,3 +346,11 @@ def test_corrupted_jpeg_iss2266(pdf, pdf_name, images, images_name, filtr):
             print(fn)  # noqa: T201
             img = Image.open(BytesIO(zf.read(fn)))
             assert image_similarity(reader.pages[p].images[i].image, img) >= 0.99
+
+
+@pytest.mark.enable_socket()
+@pytest.mark.timeout(30)
+def test_large_compressed_image():
+    url = "https://github.com/py-pdf/pypdf/files/15306199/file_with_large_compressed_image.pdf"
+    reader = PdfReader(BytesIO(get_data_from_url(url, name="file_with_large_compressed_image.pdf")))
+    list(reader.pages[0].images)