We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Barcode seperator processing fails with password protected PDFs
### Description

With PAPERLESS_CONSUMER_ENABLE_BARCODES=true uploading a password protected PDF causes the file task to fail. It appears that the barcode separation stage fails with an error rather than skipping the document. The same document imports correctly with the above settings commented out.

Traceback from the failed task:
```python
/tmp/paperless/paperless-upload-0bj7vn9g: invalid password : Traceback (most recent call last):
  File "/usr/src/paperless/src/src/django-q/django_q/cluster.py", line 454, in worker
    res = f(*task["args"], **task["kwargs"])
  File "/usr/src/paperless/src/documents/tasks.py", line 99, in consume_file
    pdf_filepath, separators = barcodes.scan_file_for_separating_barcodes(path)
  File "/usr/src/paperless/src/documents/barcodes.py", line 121, in scan_file_for_separating_barcodes
    pdf = Pdf.open(pdf_filepath)
  File "/usr/local/lib/python3.9/site-packages/pikepdf/_methods.py", line 791, in open
    pdf = Pdf._open(
pikepdf._qpdf.PasswordError: /tmp/paperless/paperless-upload-0bj7vn9g: invalid password
```

### Steps to reproduce

1. Launch paperless with PAPERLESS_CONSUMER_ENABLE_BARCODES=true
2. Upload password protected PDF
3. Check the file tasks menu for failed jobs

### Webserver logs

_No response_

### Paperless-ngx version

1.9.2

### Host OS

Fedora Server 36

### Installation method

Other (please describe above)

### Browser

_No response_

### Configuration changes

_No response_

### Other

Official docker image running under root podman

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/documents/barcodes.py`
Content:
```
1 import logging
2 import os
3 import shutil
4 import tempfile
5 from functools import lru_cache
6 from typing import List
7 from typing import Optional
8 from typing import Tuple
9 
10 import magic
11 from django.conf import settings
12 from pdf2image import convert_from_path
13 from pikepdf import Page
14 from pikepdf import Pdf
15 from pikepdf import PdfImage
16 from PIL import Image
17 from PIL import ImageSequence
18 from pyzbar import pyzbar
19 
20 logger = logging.getLogger("paperless.barcodes")
21 
22 
23 class BarcodeImageFormatError(Exception):
24     pass
25 
26 
27 @lru_cache(maxsize=8)
28 def supported_file_type(mime_type) -> bool:
29     """
30     Determines if the file is valid for barcode
31     processing, based on MIME type and settings
32 
33     :return: True if the file is supported, False otherwise
34     """
35     supported_mime = ["application/pdf"]
36     if settings.CONSUMER_BARCODE_TIFF_SUPPORT:
37         supported_mime += ["image/tiff"]
38 
39     return mime_type in supported_mime
40 
41 
42 def barcode_reader(image: Image) -> List[str]:
43     """
44     Read any barcodes contained in image
45     Returns a list containing all found barcodes
46     """
47     barcodes = []
48     # Decode the barcode image
49     detected_barcodes = pyzbar.decode(image)
50 
51     if detected_barcodes:
52         # Traverse through all the detected barcodes in image
53         for barcode in detected_barcodes:
54             if barcode.data:
55                 decoded_barcode = barcode.data.decode("utf-8")
56                 barcodes.append(decoded_barcode)
57                 logger.debug(
58                     f"Barcode of type {str(barcode.type)} found: {decoded_barcode}",
59                 )
60     return barcodes
61 
62 
63 def get_file_mime_type(path: str) -> str:
64     """
65     Determines the file type, based on MIME type.
66 
67     Returns the MIME type.
68     """
69     mime_type = magic.from_file(path, mime=True)
70     logger.debug(f"Detected mime type: {mime_type}")
71     return mime_type
72 
73 
74 def convert_from_tiff_to_pdf(filepath: str) -> str:
75     """
76     converts a given TIFF image file to pdf into a temporary directory.
77 
78     Returns the new pdf file.
79     """
80     file_name = os.path.splitext(os.path.basename(filepath))[0]
81     mime_type = get_file_mime_type(filepath)
82     tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
83     # use old file name with pdf extension
84     if mime_type == "image/tiff":
85         newpath = os.path.join(tempdir, file_name + ".pdf")
86     else:
87         logger.warning(
88             f"Cannot convert mime type {str(mime_type)} from {str(filepath)} to pdf.",
89         )
90         return None
91     with Image.open(filepath) as image:
92         images = []
93         for i, page in enumerate(ImageSequence.Iterator(image)):
94             page = page.convert("RGB")
95             images.append(page)
96         try:
97             if len(images) == 1:
98                 images[0].save(newpath)
99             else:
100                 images[0].save(newpath, save_all=True, append_images=images[1:])
101         except OSError as e:
102             logger.warning(
103                 f"Could not save the file as pdf. Error: {str(e)}",
104             )
105             return None
106     return newpath
107 
108 
109 def scan_file_for_separating_barcodes(filepath: str) -> Tuple[Optional[str], List[int]]:
110     """
111     Scan the provided pdf file for page separating barcodes
112     Returns a PDF filepath and a list of pagenumbers,
113     which separate the file into new files
114     """
115 
116     def _pikepdf_barcode_scan(pdf_filepath: str):
117         with Pdf.open(pdf_filepath) as pdf:
118             for page_num, page in enumerate(pdf.pages):
119                 for image_key in page.images:
120                     pdfimage = PdfImage(page.images[image_key])
121 
122                     if "/CCITTFaxDecode" in pdfimage.filters:
123                         raise BarcodeImageFormatError()
124 
125                     # Not all images can be transcoded to a PIL image, which
126                     # is what pyzbar expects to receive
127                     pillow_img = pdfimage.as_pil_image()
128 
129                     detected_barcodes = barcode_reader(pillow_img)
130 
131                     if settings.CONSUMER_BARCODE_STRING in detected_barcodes:
132                         separator_page_numbers.append(page_num)
133 
134     def _pdf2image_barcode_scan(pdf_filepath: str):
135         # use a temporary directory in case the file os too big to handle in memory
136         with tempfile.TemporaryDirectory() as path:
137             pages_from_path = convert_from_path(pdf_filepath, output_folder=path)
138             for current_page_number, page in enumerate(pages_from_path):
139                 current_barcodes = barcode_reader(page)
140                 if settings.CONSUMER_BARCODE_STRING in current_barcodes:
141                     separator_page_numbers.append(current_page_number)
142 
143     separator_page_numbers = []
144     pdf_filepath = None
145 
146     mime_type = get_file_mime_type(filepath)
147 
148     if supported_file_type(mime_type):
149         pdf_filepath = filepath
150         if mime_type == "image/tiff":
151             pdf_filepath = convert_from_tiff_to_pdf(filepath)
152 
153         if settings.CONSUMER_USE_LEGACY_DETECTION:
154             _pdf2image_barcode_scan(pdf_filepath)
155         else:
156             try:
157                 _pikepdf_barcode_scan(pdf_filepath)
158             except Exception as e:
159 
160                 logger.warning(
161                     f"Exception using pikepdf for barcodes,"
162                     f" falling back to pdf2image: {e}",
163                 )
164                 # Reset this incase pikepdf got part way through
165                 separator_page_numbers = []
166                 _pdf2image_barcode_scan(pdf_filepath)
167 
168     else:
169         logger.warning(
170             f"Unsupported file format for barcode reader: {str(mime_type)}",
171         )
172     return pdf_filepath, separator_page_numbers
173 
174 
175 def separate_pages(filepath: str, pages_to_split_on: List[int]) -> List[str]:
176     """
177     Separate the provided pdf file on the pages_to_split_on.
178     The pages which are defined by page_numbers will be removed.
179     Returns a list of (temporary) filepaths to consume.
180     These will need to be deleted later.
181     """
182 
183     document_paths = []
184 
185     if not pages_to_split_on:
186         logger.warning("No pages to split on!")
187         return document_paths
188 
189     os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
190     tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
191     fname = os.path.splitext(os.path.basename(filepath))[0]
192     pdf = Pdf.open(filepath)
193 
194     # A list of documents, ie a list of lists of pages
195     documents: List[List[Page]] = []
196     # A single document, ie a list of pages
197     document: List[Page] = []
198 
199     for idx, page in enumerate(pdf.pages):
200         # Keep building the new PDF as long as it is not a
201         # separator index
202         if idx not in pages_to_split_on:
203             document.append(page)
204             # Make sure to append the very last document to the documents
205             if idx == (len(pdf.pages) - 1):
206                 documents.append(document)
207                 document = []
208         else:
209             # This is a split index, save the current PDF pages, and restart
210             # a new destination page listing
211             logger.debug(f"Starting new document at idx {idx}")
212             documents.append(document)
213             document = []
214 
215     documents = [x for x in documents if len(x)]
216 
217     logger.debug(f"Split into {len(documents)} new documents")
218 
219     # Write the new documents out
220     for doc_idx, document in enumerate(documents):
221         dst = Pdf.new()
222         dst.pages.extend(document)
223 
224         output_filename = f"{fname}_document_{doc_idx}.pdf"
225 
226         logger.debug(f"pdf no:{doc_idx} has {len(dst.pages)} pages")
227         savepath = os.path.join(tempdir, output_filename)
228         with open(savepath, "wb") as out:
229             dst.save(out)
230         document_paths.append(savepath)
231 
232     return document_paths
233 
234 
235 def save_to_dir(
236     filepath: str,
237     newname: str = None,
238     target_dir: str = settings.CONSUMPTION_DIR,
239 ):
240     """
241     Copies filepath to target_dir.
242     Optionally rename the file.
243     """
244     if os.path.isfile(filepath) and os.path.isdir(target_dir):
245         dst = shutil.copy(filepath, target_dir)
246         logging.debug(f"saved {str(filepath)} to {str(dst)}")
247         if newname:
248             dst_new = os.path.join(target_dir, newname)
249             logger.debug(f"moving {str(dst)} to {str(dst_new)}")
250             os.rename(dst, dst_new)
251     else:
252         logger.warning(f"{str(filepath)} or {str(target_dir)} don't exist.")
253
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/documents/barcodes.py b/src/documents/barcodes.py
--- a/src/documents/barcodes.py
+++ b/src/documents/barcodes.py
@@ -10,9 +10,12 @@
 import magic
 from django.conf import settings
 from pdf2image import convert_from_path
+from pdf2image.exceptions import PDFPageCountError
 from pikepdf import Page
+from pikepdf import PasswordError
 from pikepdf import Pdf
 from pikepdf import PdfImage
+from pikepdf.models.image import HifiPrintImageNotTranscodableError
 from PIL import Image
 from PIL import ImageSequence
 from pyzbar import pyzbar
@@ -120,7 +123,9 @@
                     pdfimage = PdfImage(page.images[image_key])
 
                     if "/CCITTFaxDecode" in pdfimage.filters:
-                        raise BarcodeImageFormatError()
+                        raise BarcodeImageFormatError(
+                            "Unable to decode CCITTFaxDecode images",
+                        )
 
                     # Not all images can be transcoded to a PIL image, which
                     # is what pyzbar expects to receive
@@ -132,7 +137,7 @@
                         separator_page_numbers.append(page_num)
 
     def _pdf2image_barcode_scan(pdf_filepath: str):
-        # use a temporary directory in case the file os too big to handle in memory
+        # use a temporary directory in case the file is too big to handle in memory
         with tempfile.TemporaryDirectory() as path:
             pages_from_path = convert_from_path(pdf_filepath, output_folder=path)
             for current_page_number, page in enumerate(pages_from_path):
@@ -150,20 +155,42 @@
         if mime_type == "image/tiff":
             pdf_filepath = convert_from_tiff_to_pdf(filepath)
 
+        # Chose the scanner
         if settings.CONSUMER_USE_LEGACY_DETECTION:
-            _pdf2image_barcode_scan(pdf_filepath)
+            logger.debug("Using pdf2image for barcodes")
+            scanner_function = _pdf2image_barcode_scan
         else:
-            try:
-                _pikepdf_barcode_scan(pdf_filepath)
-            except Exception as e:
+            logger.debug("Using pikepdf for barcodes")
+            scanner_function = _pikepdf_barcode_scan
 
-                logger.warning(
-                    f"Exception using pikepdf for barcodes,"
-                    f" falling back to pdf2image: {e}",
-                )
-                # Reset this incase pikepdf got part way through
+        # Run the scanner
+        try:
+            scanner_function(pdf_filepath)
+        # Neither method can handle password protected PDFs without it being
+        # provided.  Log it and continue
+        except (PasswordError, PDFPageCountError) as e:
+            logger.warning(
+                f"File is likely password protected, not splitting: {e}",
+            )
+        # Handle pikepdf related image decoding issues with a fallback
+        except (BarcodeImageFormatError, HifiPrintImageNotTranscodableError) as e:
+            logger.warning(
+                f"Falling back to pdf2image because: {e}",
+            )
+            try:
                 separator_page_numbers = []
                 _pdf2image_barcode_scan(pdf_filepath)
+            # This file is really borked, allow the consumption to continue
+            # but it may fail further on
+            except Exception as e:  # pragma: no cover
+                logger.warning(
+                    f"Exception during barcode scanning: {e}",
+                )
+        # We're not sure what happened, but allow the consumption to continue
+        except Exception as e:  # pragma: no cover
+            logger.warning(
+                f"Exception during barcode scanning: {e}",
+            )
 
     else:
         logger.warning(

{"golden_diff": "diff --git a/src/documents/barcodes.py b/src/documents/barcodes.py\n--- a/src/documents/barcodes.py\n+++ b/src/documents/barcodes.py\n@@ -10,9 +10,12 @@\n import magic\n from django.conf import settings\n from pdf2image import convert_from_path\n+from pdf2image.exceptions import PDFPageCountError\n from pikepdf import Page\n+from pikepdf import PasswordError\n from pikepdf import Pdf\n from pikepdf import PdfImage\n+from pikepdf.models.image import HifiPrintImageNotTranscodableError\n from PIL import Image\n from PIL import ImageSequence\n from pyzbar import pyzbar\n@@ -120,7 +123,9 @@\n                     pdfimage = PdfImage(page.images[image_key])\n \n                     if \"/CCITTFaxDecode\" in pdfimage.filters:\n-                        raise BarcodeImageFormatError()\n+                        raise BarcodeImageFormatError(\n+                            \"Unable to decode CCITTFaxDecode images\",\n+                        )\n \n                     # Not all images can be transcoded to a PIL image, which\n                     # is what pyzbar expects to receive\n@@ -132,7 +137,7 @@\n                         separator_page_numbers.append(page_num)\n \n     def _pdf2image_barcode_scan(pdf_filepath: str):\n-        # use a temporary directory in case the file os too big to handle in memory\n+        # use a temporary directory in case the file is too big to handle in memory\n         with tempfile.TemporaryDirectory() as path:\n             pages_from_path = convert_from_path(pdf_filepath, output_folder=path)\n             for current_page_number, page in enumerate(pages_from_path):\n@@ -150,20 +155,42 @@\n         if mime_type == \"image/tiff\":\n             pdf_filepath = convert_from_tiff_to_pdf(filepath)\n \n+        # Chose the scanner\n         if settings.CONSUMER_USE_LEGACY_DETECTION:\n-            _pdf2image_barcode_scan(pdf_filepath)\n+            logger.debug(\"Using pdf2image for barcodes\")\n+            scanner_function = _pdf2image_barcode_scan\n         else:\n-            try:\n-                _pikepdf_barcode_scan(pdf_filepath)\n-            except Exception as e:\n+            logger.debug(\"Using pikepdf for barcodes\")\n+            scanner_function = _pikepdf_barcode_scan\n \n-                logger.warning(\n-                    f\"Exception using pikepdf for barcodes,\"\n-                    f\" falling back to pdf2image: {e}\",\n-                )\n-                # Reset this incase pikepdf got part way through\n+        # Run the scanner\n+        try:\n+            scanner_function(pdf_filepath)\n+        # Neither method can handle password protected PDFs without it being\n+        # provided.  Log it and continue\n+        except (PasswordError, PDFPageCountError) as e:\n+            logger.warning(\n+                f\"File is likely password protected, not splitting: {e}\",\n+            )\n+        # Handle pikepdf related image decoding issues with a fallback\n+        except (BarcodeImageFormatError, HifiPrintImageNotTranscodableError) as e:\n+            logger.warning(\n+                f\"Falling back to pdf2image because: {e}\",\n+            )\n+            try:\n                 separator_page_numbers = []\n                 _pdf2image_barcode_scan(pdf_filepath)\n+            # This file is really borked, allow the consumption to continue\n+            # but it may fail further on\n+            except Exception as e:  # pragma: no cover\n+                logger.warning(\n+                    f\"Exception during barcode scanning: {e}\",\n+                )\n+        # We're not sure what happened, but allow the consumption to continue\n+        except Exception as e:  # pragma: no cover\n+            logger.warning(\n+                f\"Exception during barcode scanning: {e}\",\n+            )\n \n     else:\n         logger.warning(\n", "issue": "[BUG] Barcode seperator processing fails with password protected PDFs\n### Description\n\nWith PAPERLESS_CONSUMER_ENABLE_BARCODES=true uploading a password protected PDF causes the file task to fail. It appears that the barcode separation stage fails with an error rather than skipping the document. The same document imports correctly with the above settings commented out.\r\n\r\nTraceback from the failed task:\r\n```python\r\n/tmp/paperless/paperless-upload-0bj7vn9g: invalid password : Traceback (most recent call last):\r\n  File \"/usr/src/paperless/src/src/django-q/django_q/cluster.py\", line 454, in worker\r\n    res = f(*task[\"args\"], **task[\"kwargs\"])\r\n  File \"/usr/src/paperless/src/documents/tasks.py\", line 99, in consume_file\r\n    pdf_filepath, separators = barcodes.scan_file_for_separating_barcodes(path)\r\n  File \"/usr/src/paperless/src/documents/barcodes.py\", line 121, in scan_file_for_separating_barcodes\r\n    pdf = Pdf.open(pdf_filepath)\r\n  File \"/usr/local/lib/python3.9/site-packages/pikepdf/_methods.py\", line 791, in open\r\n    pdf = Pdf._open(\r\npikepdf._qpdf.PasswordError: /tmp/paperless/paperless-upload-0bj7vn9g: invalid password\r\n```\n\n### Steps to reproduce\n\n1. Launch paperless with PAPERLESS_CONSUMER_ENABLE_BARCODES=true\r\n2. Upload password protected PDF\r\n3. Check the file tasks menu for failed jobs\n\n### Webserver logs\n\n_No response_\n\n### Paperless-ngx version\n\n1.9.2\n\n### Host OS\n\nFedora Server 36\n\n### Installation method\n\nOther (please describe above)\n\n### Browser\n\n_No response_\n\n### Configuration changes\n\n_No response_\n\n### Other\n\nOfficial docker image running under root podman\n", "before_files": [{"content": "import logging\nimport os\nimport shutil\nimport tempfile\nfrom functools import lru_cache\nfrom typing import List\nfrom typing import Optional\nfrom typing import Tuple\n\nimport magic\nfrom django.conf import settings\nfrom pdf2image import convert_from_path\nfrom pikepdf import Page\nfrom pikepdf import Pdf\nfrom pikepdf import PdfImage\nfrom PIL import Image\nfrom PIL import ImageSequence\nfrom pyzbar import pyzbar\n\nlogger = logging.getLogger(\"paperless.barcodes\")\n\n\nclass BarcodeImageFormatError(Exception):\n    pass\n\n\n@lru_cache(maxsize=8)\ndef supported_file_type(mime_type) -> bool:\n    \"\"\"\n    Determines if the file is valid for barcode\n    processing, based on MIME type and settings\n\n    :return: True if the file is supported, False otherwise\n    \"\"\"\n    supported_mime = [\"application/pdf\"]\n    if settings.CONSUMER_BARCODE_TIFF_SUPPORT:\n        supported_mime += [\"image/tiff\"]\n\n    return mime_type in supported_mime\n\n\ndef barcode_reader(image: Image) -> List[str]:\n    \"\"\"\n    Read any barcodes contained in image\n    Returns a list containing all found barcodes\n    \"\"\"\n    barcodes = []\n    # Decode the barcode image\n    detected_barcodes = pyzbar.decode(image)\n\n    if detected_barcodes:\n        # Traverse through all the detected barcodes in image\n        for barcode in detected_barcodes:\n            if barcode.data:\n                decoded_barcode = barcode.data.decode(\"utf-8\")\n                barcodes.append(decoded_barcode)\n                logger.debug(\n                    f\"Barcode of type {str(barcode.type)} found: {decoded_barcode}\",\n                )\n    return barcodes\n\n\ndef get_file_mime_type(path: str) -> str:\n    \"\"\"\n    Determines the file type, based on MIME type.\n\n    Returns the MIME type.\n    \"\"\"\n    mime_type = magic.from_file(path, mime=True)\n    logger.debug(f\"Detected mime type: {mime_type}\")\n    return mime_type\n\n\ndef convert_from_tiff_to_pdf(filepath: str) -> str:\n    \"\"\"\n    converts a given TIFF image file to pdf into a temporary directory.\n\n    Returns the new pdf file.\n    \"\"\"\n    file_name = os.path.splitext(os.path.basename(filepath))[0]\n    mime_type = get_file_mime_type(filepath)\n    tempdir = tempfile.mkdtemp(prefix=\"paperless-\", dir=settings.SCRATCH_DIR)\n    # use old file name with pdf extension\n    if mime_type == \"image/tiff\":\n        newpath = os.path.join(tempdir, file_name + \".pdf\")\n    else:\n        logger.warning(\n            f\"Cannot convert mime type {str(mime_type)} from {str(filepath)} to pdf.\",\n        )\n        return None\n    with Image.open(filepath) as image:\n        images = []\n        for i, page in enumerate(ImageSequence.Iterator(image)):\n            page = page.convert(\"RGB\")\n            images.append(page)\n        try:\n            if len(images) == 1:\n                images[0].save(newpath)\n            else:\n                images[0].save(newpath, save_all=True, append_images=images[1:])\n        except OSError as e:\n            logger.warning(\n                f\"Could not save the file as pdf. Error: {str(e)}\",\n            )\n            return None\n    return newpath\n\n\ndef scan_file_for_separating_barcodes(filepath: str) -> Tuple[Optional[str], List[int]]:\n    \"\"\"\n    Scan the provided pdf file for page separating barcodes\n    Returns a PDF filepath and a list of pagenumbers,\n    which separate the file into new files\n    \"\"\"\n\n    def _pikepdf_barcode_scan(pdf_filepath: str):\n        with Pdf.open(pdf_filepath) as pdf:\n            for page_num, page in enumerate(pdf.pages):\n                for image_key in page.images:\n                    pdfimage = PdfImage(page.images[image_key])\n\n                    if \"/CCITTFaxDecode\" in pdfimage.filters:\n                        raise BarcodeImageFormatError()\n\n                    # Not all images can be transcoded to a PIL image, which\n                    # is what pyzbar expects to receive\n                    pillow_img = pdfimage.as_pil_image()\n\n                    detected_barcodes = barcode_reader(pillow_img)\n\n                    if settings.CONSUMER_BARCODE_STRING in detected_barcodes:\n                        separator_page_numbers.append(page_num)\n\n    def _pdf2image_barcode_scan(pdf_filepath: str):\n        # use a temporary directory in case the file os too big to handle in memory\n        with tempfile.TemporaryDirectory() as path:\n            pages_from_path = convert_from_path(pdf_filepath, output_folder=path)\n            for current_page_number, page in enumerate(pages_from_path):\n                current_barcodes = barcode_reader(page)\n                if settings.CONSUMER_BARCODE_STRING in current_barcodes:\n                    separator_page_numbers.append(current_page_number)\n\n    separator_page_numbers = []\n    pdf_filepath = None\n\n    mime_type = get_file_mime_type(filepath)\n\n    if supported_file_type(mime_type):\n        pdf_filepath = filepath\n        if mime_type == \"image/tiff\":\n            pdf_filepath = convert_from_tiff_to_pdf(filepath)\n\n        if settings.CONSUMER_USE_LEGACY_DETECTION:\n            _pdf2image_barcode_scan(pdf_filepath)\n        else:\n            try:\n                _pikepdf_barcode_scan(pdf_filepath)\n            except Exception as e:\n\n                logger.warning(\n                    f\"Exception using pikepdf for barcodes,\"\n                    f\" falling back to pdf2image: {e}\",\n                )\n                # Reset this incase pikepdf got part way through\n                separator_page_numbers = []\n                _pdf2image_barcode_scan(pdf_filepath)\n\n    else:\n        logger.warning(\n            f\"Unsupported file format for barcode reader: {str(mime_type)}\",\n        )\n    return pdf_filepath, separator_page_numbers\n\n\ndef separate_pages(filepath: str, pages_to_split_on: List[int]) -> List[str]:\n    \"\"\"\n    Separate the provided pdf file on the pages_to_split_on.\n    The pages which are defined by page_numbers will be removed.\n    Returns a list of (temporary) filepaths to consume.\n    These will need to be deleted later.\n    \"\"\"\n\n    document_paths = []\n\n    if not pages_to_split_on:\n        logger.warning(\"No pages to split on!\")\n        return document_paths\n\n    os.makedirs(settings.SCRATCH_DIR, exist_ok=True)\n    tempdir = tempfile.mkdtemp(prefix=\"paperless-\", dir=settings.SCRATCH_DIR)\n    fname = os.path.splitext(os.path.basename(filepath))[0]\n    pdf = Pdf.open(filepath)\n\n    # A list of documents, ie a list of lists of pages\n    documents: List[List[Page]] = []\n    # A single document, ie a list of pages\n    document: List[Page] = []\n\n    for idx, page in enumerate(pdf.pages):\n        # Keep building the new PDF as long as it is not a\n        # separator index\n        if idx not in pages_to_split_on:\n            document.append(page)\n            # Make sure to append the very last document to the documents\n            if idx == (len(pdf.pages) - 1):\n                documents.append(document)\n                document = []\n        else:\n            # This is a split index, save the current PDF pages, and restart\n            # a new destination page listing\n            logger.debug(f\"Starting new document at idx {idx}\")\n            documents.append(document)\n            document = []\n\n    documents = [x for x in documents if len(x)]\n\n    logger.debug(f\"Split into {len(documents)} new documents\")\n\n    # Write the new documents out\n    for doc_idx, document in enumerate(documents):\n        dst = Pdf.new()\n        dst.pages.extend(document)\n\n        output_filename = f\"{fname}_document_{doc_idx}.pdf\"\n\n        logger.debug(f\"pdf no:{doc_idx} has {len(dst.pages)} pages\")\n        savepath = os.path.join(tempdir, output_filename)\n        with open(savepath, \"wb\") as out:\n            dst.save(out)\n        document_paths.append(savepath)\n\n    return document_paths\n\n\ndef save_to_dir(\n    filepath: str,\n    newname: str = None,\n    target_dir: str = settings.CONSUMPTION_DIR,\n):\n    \"\"\"\n    Copies filepath to target_dir.\n    Optionally rename the file.\n    \"\"\"\n    if os.path.isfile(filepath) and os.path.isdir(target_dir):\n        dst = shutil.copy(filepath, target_dir)\n        logging.debug(f\"saved {str(filepath)} to {str(dst)}\")\n        if newname:\n            dst_new = os.path.join(target_dir, newname)\n            logger.debug(f\"moving {str(dst)} to {str(dst_new)}\")\n            os.rename(dst, dst_new)\n    else:\n        logger.warning(f\"{str(filepath)} or {str(target_dir)} don't exist.\")\n", "path": "src/documents/barcodes.py"}], "after_files": [{"content": "import logging\nimport os\nimport shutil\nimport tempfile\nfrom functools import lru_cache\nfrom typing import List\nfrom typing import Optional\nfrom typing import Tuple\n\nimport magic\nfrom django.conf import settings\nfrom pdf2image import convert_from_path\nfrom pdf2image.exceptions import PDFPageCountError\nfrom pikepdf import Page\nfrom pikepdf import PasswordError\nfrom pikepdf import Pdf\nfrom pikepdf import PdfImage\nfrom pikepdf.models.image import HifiPrintImageNotTranscodableError\nfrom PIL import Image\nfrom PIL import ImageSequence\nfrom pyzbar import pyzbar\n\nlogger = logging.getLogger(\"paperless.barcodes\")\n\n\nclass BarcodeImageFormatError(Exception):\n    pass\n\n\n@lru_cache(maxsize=8)\ndef supported_file_type(mime_type) -> bool:\n    \"\"\"\n    Determines if the file is valid for barcode\n    processing, based on MIME type and settings\n\n    :return: True if the file is supported, False otherwise\n    \"\"\"\n    supported_mime = [\"application/pdf\"]\n    if settings.CONSUMER_BARCODE_TIFF_SUPPORT:\n        supported_mime += [\"image/tiff\"]\n\n    return mime_type in supported_mime\n\n\ndef barcode_reader(image: Image) -> List[str]:\n    \"\"\"\n    Read any barcodes contained in image\n    Returns a list containing all found barcodes\n    \"\"\"\n    barcodes = []\n    # Decode the barcode image\n    detected_barcodes = pyzbar.decode(image)\n\n    if detected_barcodes:\n        # Traverse through all the detected barcodes in image\n        for barcode in detected_barcodes:\n            if barcode.data:\n                decoded_barcode = barcode.data.decode(\"utf-8\")\n                barcodes.append(decoded_barcode)\n                logger.debug(\n                    f\"Barcode of type {str(barcode.type)} found: {decoded_barcode}\",\n                )\n    return barcodes\n\n\ndef get_file_mime_type(path: str) -> str:\n    \"\"\"\n    Determines the file type, based on MIME type.\n\n    Returns the MIME type.\n    \"\"\"\n    mime_type = magic.from_file(path, mime=True)\n    logger.debug(f\"Detected mime type: {mime_type}\")\n    return mime_type\n\n\ndef convert_from_tiff_to_pdf(filepath: str) -> str:\n    \"\"\"\n    converts a given TIFF image file to pdf into a temporary directory.\n\n    Returns the new pdf file.\n    \"\"\"\n    file_name = os.path.splitext(os.path.basename(filepath))[0]\n    mime_type = get_file_mime_type(filepath)\n    tempdir = tempfile.mkdtemp(prefix=\"paperless-\", dir=settings.SCRATCH_DIR)\n    # use old file name with pdf extension\n    if mime_type == \"image/tiff\":\n        newpath = os.path.join(tempdir, file_name + \".pdf\")\n    else:\n        logger.warning(\n            f\"Cannot convert mime type {str(mime_type)} from {str(filepath)} to pdf.\",\n        )\n        return None\n    with Image.open(filepath) as image:\n        images = []\n        for i, page in enumerate(ImageSequence.Iterator(image)):\n            page = page.convert(\"RGB\")\n            images.append(page)\n        try:\n            if len(images) == 1:\n                images[0].save(newpath)\n            else:\n                images[0].save(newpath, save_all=True, append_images=images[1:])\n        except OSError as e:\n            logger.warning(\n                f\"Could not save the file as pdf. Error: {str(e)}\",\n            )\n            return None\n    return newpath\n\n\ndef scan_file_for_separating_barcodes(filepath: str) -> Tuple[Optional[str], List[int]]:\n    \"\"\"\n    Scan the provided pdf file for page separating barcodes\n    Returns a PDF filepath and a list of pagenumbers,\n    which separate the file into new files\n    \"\"\"\n\n    def _pikepdf_barcode_scan(pdf_filepath: str):\n        with Pdf.open(pdf_filepath) as pdf:\n            for page_num, page in enumerate(pdf.pages):\n                for image_key in page.images:\n                    pdfimage = PdfImage(page.images[image_key])\n\n                    if \"/CCITTFaxDecode\" in pdfimage.filters:\n                        raise BarcodeImageFormatError(\n                            \"Unable to decode CCITTFaxDecode images\",\n                        )\n\n                    # Not all images can be transcoded to a PIL image, which\n                    # is what pyzbar expects to receive\n                    pillow_img = pdfimage.as_pil_image()\n\n                    detected_barcodes = barcode_reader(pillow_img)\n\n                    if settings.CONSUMER_BARCODE_STRING in detected_barcodes:\n                        separator_page_numbers.append(page_num)\n\n    def _pdf2image_barcode_scan(pdf_filepath: str):\n        # use a temporary directory in case the file is too big to handle in memory\n        with tempfile.TemporaryDirectory() as path:\n            pages_from_path = convert_from_path(pdf_filepath, output_folder=path)\n            for current_page_number, page in enumerate(pages_from_path):\n                current_barcodes = barcode_reader(page)\n                if settings.CONSUMER_BARCODE_STRING in current_barcodes:\n                    separator_page_numbers.append(current_page_number)\n\n    separator_page_numbers = []\n    pdf_filepath = None\n\n    mime_type = get_file_mime_type(filepath)\n\n    if supported_file_type(mime_type):\n        pdf_filepath = filepath\n        if mime_type == \"image/tiff\":\n            pdf_filepath = convert_from_tiff_to_pdf(filepath)\n\n        # Chose the scanner\n        if settings.CONSUMER_USE_LEGACY_DETECTION:\n            logger.debug(\"Using pdf2image for barcodes\")\n            scanner_function = _pdf2image_barcode_scan\n        else:\n            logger.debug(\"Using pikepdf for barcodes\")\n            scanner_function = _pikepdf_barcode_scan\n\n        # Run the scanner\n        try:\n            scanner_function(pdf_filepath)\n        # Neither method can handle password protected PDFs without it being\n        # provided.  Log it and continue\n        except (PasswordError, PDFPageCountError) as e:\n            logger.warning(\n                f\"File is likely password protected, not splitting: {e}\",\n            )\n        # Handle pikepdf related image decoding issues with a fallback\n        except (BarcodeImageFormatError, HifiPrintImageNotTranscodableError) as e:\n            logger.warning(\n                f\"Falling back to pdf2image because: {e}\",\n            )\n            try:\n                separator_page_numbers = []\n                _pdf2image_barcode_scan(pdf_filepath)\n            # This file is really borked, allow the consumption to continue\n            # but it may fail further on\n            except Exception as e:  # pragma: no cover\n                logger.warning(\n                    f\"Exception during barcode scanning: {e}\",\n                )\n        # We're not sure what happened, but allow the consumption to continue\n        except Exception as e:  # pragma: no cover\n            logger.warning(\n                f\"Exception during barcode scanning: {e}\",\n            )\n\n    else:\n        logger.warning(\n            f\"Unsupported file format for barcode reader: {str(mime_type)}\",\n        )\n    return pdf_filepath, separator_page_numbers\n\n\ndef separate_pages(filepath: str, pages_to_split_on: List[int]) -> List[str]:\n    \"\"\"\n    Separate the provided pdf file on the pages_to_split_on.\n    The pages which are defined by page_numbers will be removed.\n    Returns a list of (temporary) filepaths to consume.\n    These will need to be deleted later.\n    \"\"\"\n\n    document_paths = []\n\n    if not pages_to_split_on:\n        logger.warning(\"No pages to split on!\")\n        return document_paths\n\n    os.makedirs(settings.SCRATCH_DIR, exist_ok=True)\n    tempdir = tempfile.mkdtemp(prefix=\"paperless-\", dir=settings.SCRATCH_DIR)\n    fname = os.path.splitext(os.path.basename(filepath))[0]\n    pdf = Pdf.open(filepath)\n\n    # A list of documents, ie a list of lists of pages\n    documents: List[List[Page]] = []\n    # A single document, ie a list of pages\n    document: List[Page] = []\n\n    for idx, page in enumerate(pdf.pages):\n        # Keep building the new PDF as long as it is not a\n        # separator index\n        if idx not in pages_to_split_on:\n            document.append(page)\n            # Make sure to append the very last document to the documents\n            if idx == (len(pdf.pages) - 1):\n                documents.append(document)\n                document = []\n        else:\n            # This is a split index, save the current PDF pages, and restart\n            # a new destination page listing\n            logger.debug(f\"Starting new document at idx {idx}\")\n            documents.append(document)\n            document = []\n\n    documents = [x for x in documents if len(x)]\n\n    logger.debug(f\"Split into {len(documents)} new documents\")\n\n    # Write the new documents out\n    for doc_idx, document in enumerate(documents):\n        dst = Pdf.new()\n        dst.pages.extend(document)\n\n        output_filename = f\"{fname}_document_{doc_idx}.pdf\"\n\n        logger.debug(f\"pdf no:{doc_idx} has {len(dst.pages)} pages\")\n        savepath = os.path.join(tempdir, output_filename)\n        with open(savepath, \"wb\") as out:\n            dst.save(out)\n        document_paths.append(savepath)\n\n    return document_paths\n\n\ndef save_to_dir(\n    filepath: str,\n    newname: str = None,\n    target_dir: str = settings.CONSUMPTION_DIR,\n):\n    \"\"\"\n    Copies filepath to target_dir.\n    Optionally rename the file.\n    \"\"\"\n    if os.path.isfile(filepath) and os.path.isdir(target_dir):\n        dst = shutil.copy(filepath, target_dir)\n        logging.debug(f\"saved {str(filepath)} to {str(dst)}\")\n        if newname:\n            dst_new = os.path.join(target_dir, newname)\n            logger.debug(f\"moving {str(dst)} to {str(dst_new)}\")\n            os.rename(dst, dst_new)\n    else:\n        logger.warning(f\"{str(filepath)} or {str(target_dir)} don't exist.\")\n", "path": "src/documents/barcodes.py"}]}