]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
feature: Add support for zxing as barcode scanning lib
authorMarvin Gaube <dev@marvingaube.de>
Thu, 16 Mar 2023 20:15:10 +0000 (21:15 +0100)
committerMarvin Gaube <dev@marvingaube.de>
Sun, 19 Mar 2023 12:48:35 +0000 (13:48 +0100)
Pipfile
Pipfile.lock
docs/configuration.md
src/documents/barcodes.py
src/documents/tests/test_barcodes.py
src/paperless/checks.py
src/paperless/settings.py

diff --git a/Pipfile b/Pipfile
index 8cf90a5dc5eee62a7beab6c0dd8b7bdc0536b7b8..cfc32071c2ea8f6fcd1f3b490d9b06796cfb8bd7 100644 (file)
--- a/Pipfile
+++ b/Pipfile
@@ -65,6 +65,7 @@ scipy = "==1.8.1"
 # Locked version until https://github.com/django/channels_redis/issues/332
 # is resolved
 channels-redis = "==3.4.1"
+zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"}
 
 [dev-packages]
 coveralls = "*"
index caf5b9a08866d9557c8f6a024a74fb0bb202db72..21969192556f3ff2eb52e382df815702f73879c0 100644 (file)
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "8b3f3443de30aecc7c893d4a5a78123ba17e3dc10eed6042450a9c0cf6afcc3f"
+            "sha256": "d813537b3e32ac288b7a89f85041b1b52b4bf69b349dd0df4a1283dc17ce2275"
         },
         "pipfile-spec": 6,
         "requires": {},
                 "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
                 "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
             ],
-            "markers": "python_version < '3.10'",
+            "markers": "python_version >= '3.7'",
             "version": "==4.5.0"
         },
         "tzdata": {
             ],
             "markers": "python_version >= '3.6'",
             "version": "==0.20.0"
+        },
+        "zxing-cpp": {
+            "hashes": [
+                "sha256:1b67b221aae15aad9b5609d99c38d57875bc0a4fef864142d7ca37e9ee7880b0",
+                "sha256:1d665c45029346c70ae3df5dbc36f6335ffe4f275e98dc43772fa32a65844196",
+                "sha256:214a6a0e49b92fda8d2761c74f5bfd24a677b9bf1d0ef0e083412486af97faa9",
+                "sha256:54282d0e5c573754049113a0cdbf14cc1c6b986432a367d8a788112afa92a1d5",
+                "sha256:5ce391f21763f00d5be3431e16d075e263e4b9205c2cf55d708625cb234b1f15",
+                "sha256:5fd89065f620d6b78281308c6abfb760d95760a1c9b88eb7ac612b52b331bd41",
+                "sha256:631a0c783ad233c85295e0cf4cd7740f1fe2853124c61b1ef6bcf7eb5d2fa5e6",
+                "sha256:76caafb8fc1e12c2e5ec33ce4f340a0e15e9a2aabfbfeaec170e8a2b405b8a77",
+                "sha256:8da9c912cca5829eedb2800ce3eaa1b1e52742f536aa9e798be69bf09639f399",
+                "sha256:95dd06dc559f53c1ca0eb59dbaebd802ebc839937baaf2f8d2b3def3e814c07f",
+                "sha256:97919f07c62edf1c8e0722fd64893057ce636b7067cf47bd593e98cc7e404d74",
+                "sha256:9f0c2c03f5df470ef71a7590be5042161e7590da767d4260a6d0d61a3fa80b88",
+                "sha256:a788551ddf3a6ba1152ff9a0b81d57018a3cc586544087c39d881428745faf1f",
+                "sha256:ea54fd242f93eea7bf039a68287e5e57fdf77d78e3bd5b4cbb2d289bb3380d63",
+                "sha256:f0eefdfad91e15e3f5b7ed16d83806a36f96ca482f4b042baa6297784a58b0b3",
+                "sha256:f70eefa5dc1fd9238087c024ef22f3d99ba79cb932a2c5bc5b0f1e152037722e"
+            ],
+            "index": "pypi",
+            "markers": "platform_machine == 'x86_64'",
+            "version": "==2.0.0"
         }
     },
     "develop": {
                 "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
                 "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
             ],
-            "markers": "python_version < '3.10'",
+            "markers": "python_version >= '3.7'",
             "version": "==4.5.0"
         },
         "urllib3": {
                 "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
                 "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
             ],
-            "markers": "python_version < '3.10'",
+            "markers": "python_version >= '3.7'",
             "version": "==4.5.0"
         },
         "urllib3": {
index d3b391f1a32c58de21e994e85bf0eeb391740400..98d7ee135e593877bfc439b1e277fcfd61b55250 100644 (file)
@@ -872,6 +872,16 @@ file, which are separated by one or multiple barcode pages.
 
     Defaults to false.
 
+`PAPERLESS_CONSUMER_BARCODE_SCANNER=<string>`
+
+: Sets the barcode scanner used for barcode functionality.
+
+    Currently, "PYZBAR" (the default) or "ZXING" might be selected.
+    If you have problems that your Barcodes/QR-Codes are not detected
+    (especially with bad scan quality and/or small codes), try the other one.
+
+    zxing is not available on all platforms.
+
 `PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT=<bool>`
 
 : Whether TIFF image files should be scanned for barcodes. This will
index 3ecf6f96a1767c9aebc6f305dba3cc41a43c58c9..1575b996623630026266cf90e854f244963b730b 100644 (file)
@@ -17,7 +17,6 @@ from pikepdf import Page
 from pikepdf import Pdf
 from PIL import Image
 from PIL import ImageSequence
-from pyzbar import pyzbar
 
 logger = logging.getLogger("paperless.barcodes")
 
@@ -83,18 +82,35 @@ def barcode_reader(image: Image) -> List[str]:
     Returns a list containing all found barcodes
     """
     barcodes = []
-    # Decode the barcode image
-    detected_barcodes = pyzbar.decode(image)
 
-    if detected_barcodes:
-        # Traverse through all the detected barcodes in image
+    if settings.CONSUMER_BARCODE_SCANNER == "PYZBAR":
+        logger.debug("Scanning for barcodes using PYZBAR")
+        from pyzbar import pyzbar
+
+        # Decode the barcode image
+        detected_barcodes = pyzbar.decode(image)
+
+        if detected_barcodes:
+            # Traverse through all the detected barcodes in image
+            for barcode in detected_barcodes:
+                if barcode.data:
+                    decoded_barcode = barcode.data.decode("utf-8")
+                    barcodes.append(decoded_barcode)
+                    logger.debug(
+                        f"Barcode of type {str(barcode.type)} found: {decoded_barcode}",
+                    )
+    elif settings.CONSUMER_BARCODE_SCANNER == "ZXING":
+        logger.debug("Scanning for barcodes using ZXING")
+        import zxingcpp
+
+        detected_barcodes = zxingcpp.read_barcodes(image)
         for barcode in detected_barcodes:
-            if barcode.data:
-                decoded_barcode = barcode.data.decode("utf-8")
-                barcodes.append(decoded_barcode)
+            if barcode.text:
+                barcodes.append(barcode.text)
                 logger.debug(
-                    f"Barcode of type {str(barcode.type)} found: {decoded_barcode}",
+                    f"Barcode of type {str(barcode.format)} found: {barcode.text}",
                 )
+
     return barcodes
 
 
index 02ed26308be600eb2c2b02166794fe1f1a9c04a9..7d7b281e9ae27436dbba0b38515717c740504afe 100644 (file)
@@ -3,6 +3,7 @@ import shutil
 from pathlib import Path
 from unittest import mock
 
+import pytest
 from django.conf import settings
 from django.test import override_settings
 from django.test import TestCase
@@ -14,6 +15,7 @@ from documents.tests.utils import FileSystemAssertsMixin
 from PIL import Image
 
 
+@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
 class TestBarcode(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
 
     SAMPLE_DIR = Path(__file__).parent / "samples"
@@ -1030,3 +1032,20 @@ class TestAsnBarcodes(DirectoriesMixin, TestCase):
                 tasks.consume_file,
                 dst,
             )
+
+
+try:
+    import zxingcpp
+
+    ZXING_AVAILIBLE = True
+except ImportError:
+    ZXING_AVAILIBLE = False
+
+
+@pytest.mark.skipif(
+    not ZXING_AVAILIBLE,
+    reason="No zxingcpp",
+)
+@override_settings(CONSUMER_BARCODE_SCANNER="ZXING")
+class TestBarcodeZxing(TestBarcode):
+    pass
index 658ec9d3130a40841b77e2bbe5b1df0823234bdd..3da37e264b64581e7bed93b72fe429c9b4bb2a71 100644 (file)
@@ -166,4 +166,17 @@ def settings_values_check(app_configs, **kwargs):
             )
         return msgs
 
-    return _ocrmypdf_settings_check() + _timezone_validate()
+    def _barcode_scanner_validate():
+        """
+        Validates the barcode scanner type
+        """
+        msgs = []
+        if settings.CONSUMER_BARCODE_SCANNER not in ["PYZBAR", "ZXING"]:
+            msgs.append(
+                Error(f'Invalid Barcode Scanner "{settings.CONSUMER_BARCODE_SCANNER}"'),
+            )
+        return msgs
+
+    return (
+        _ocrmypdf_settings_check() + _timezone_validate() + _barcode_scanner_validate()
+    )
index 6768704a0e2e165db796fc1772ba73632d622a82..ab9e1d6329f4aca347db6bd672a77103e4c3ebb3 100644 (file)
@@ -718,6 +718,12 @@ CONSUMER_BARCODE_STRING: Final[str] = os.getenv(
     "PATCHT",
 )
 
+consumer_barcode_scanner_tmp: Final[str] = os.getenv(
+    "PAPERLESS_CONSUMER_BARCODE_SCANNER",
+    "PYZBAR",
+)
+CONSUMER_BARCODE_SCANNER = consumer_barcode_scanner_tmp.upper()
+
 CONSUMER_ENABLE_ASN_BARCODE: Final[bool] = __get_boolean(
     "PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE",
 )