From e89c0f15ddaa7903773d7823d1be9cd11b875252 Mon Sep 17 00:00:00 2001 From: Marvin Gaube Date: Thu, 16 Mar 2023 21:15:10 +0100 Subject: [PATCH] feature: Add support for zxing as barcode scanning lib --- Pipfile | 1 + Pipfile.lock | 31 +++++++++++++++++++++---- docs/configuration.md | 10 ++++++++ src/documents/barcodes.py | 34 ++++++++++++++++++++-------- src/documents/tests/test_barcodes.py | 19 ++++++++++++++++ src/paperless/checks.py | 15 +++++++++++- src/paperless/settings.py | 6 +++++ 7 files changed, 102 insertions(+), 14 deletions(-) diff --git a/Pipfile b/Pipfile index 8cf90a5dc..cfc32071c 100644 --- 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 = "*" diff --git a/Pipfile.lock b/Pipfile.lock index caf5b9a08..219691925 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "8b3f3443de30aecc7c893d4a5a78123ba17e3dc10eed6042450a9c0cf6afcc3f" + "sha256": "d813537b3e32ac288b7a89f85041b1b52b4bf69b349dd0df4a1283dc17ce2275" }, "pipfile-spec": 6, "requires": {}, @@ -1912,7 +1912,7 @@ "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" ], - "markers": "python_version < '3.10'", + "markers": "python_version >= '3.7'", "version": "==4.5.0" }, "tzdata": { @@ -2261,6 +2261,29 @@ ], "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": { @@ -3181,7 +3204,7 @@ "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" ], - "markers": "python_version < '3.10'", + "markers": "python_version >= '3.7'", "version": "==4.5.0" }, "urllib3": { @@ -3745,7 +3768,7 @@ "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" ], - "markers": "python_version < '3.10'", + "markers": "python_version >= '3.7'", "version": "==4.5.0" }, "urllib3": { diff --git a/docs/configuration.md b/docs/configuration.md index d3b391f1a..98d7ee135 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -872,6 +872,16 @@ file, which are separated by one or multiple barcode pages. Defaults to false. +`PAPERLESS_CONSUMER_BARCODE_SCANNER=` + +: 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=` : Whether TIFF image files should be scanned for barcodes. This will diff --git a/src/documents/barcodes.py b/src/documents/barcodes.py index 3ecf6f96a..1575b9966 100644 --- a/src/documents/barcodes.py +++ b/src/documents/barcodes.py @@ -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 diff --git a/src/documents/tests/test_barcodes.py b/src/documents/tests/test_barcodes.py index 02ed26308..7d7b281e9 100644 --- a/src/documents/tests/test_barcodes.py +++ b/src/documents/tests/test_barcodes.py @@ -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 diff --git a/src/paperless/checks.py b/src/paperless/checks.py index 658ec9d31..3da37e264 100644 --- a/src/paperless/checks.py +++ b/src/paperless/checks.py @@ -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() + ) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 6768704a0..ab9e1d632 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -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", )