Removes all pyzbar/libzbar

This commit is contained in:
Trenton H
2026-02-10 15:53:33 -08:00
parent 21e9eaa4db
commit 15d18c06ed
15 changed files with 4 additions and 116 deletions

View File

@@ -64,8 +64,6 @@ ARG RUNTIME_PACKAGES="\
libmagic1 \ libmagic1 \
media-types \ media-types \
zlib1g \ zlib1g \
# Barcode splitter
libzbar0 \
poppler-utils \ poppler-utils \
htop \ htop \
sudo" sudo"

View File

@@ -69,7 +69,6 @@ updates:
patterns: patterns:
- "ocrmypdf" - "ocrmypdf"
- "pdf2image" - "pdf2image"
- "pyzbar"
- "zxing-cpp" - "zxing-cpp"
- "tika-client" - "tika-client"
- "gotenberg-client" - "gotenberg-client"

View File

@@ -55,7 +55,7 @@ jobs:
run: | run: |
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends \ sudo apt-get install -qq --no-install-recommends \
unpaper tesseract-ocr imagemagick ghostscript libzbar0 poppler-utils unpaper tesseract-ocr imagemagick ghostscript poppler-utils
- name: Configure ImageMagick - name: Configure ImageMagick
run: | run: |
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml

View File

@@ -20,7 +20,6 @@ src/documents/admin.py:0: error: Skipping analyzing "auditlog.models": module is
src/documents/admin.py:0: error: Skipping analyzing "treenode.admin": module is installed, but missing library stubs or py.typed marker [import-untyped] src/documents/admin.py:0: error: Skipping analyzing "treenode.admin": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/documents/barcodes.py:0: error: "Image" has no attribute "filename" [attr-defined] src/documents/barcodes.py:0: error: "Image" has no attribute "filename" [attr-defined]
src/documents/barcodes.py:0: error: Cannot find implementation or library stub for module named "zxingcpp" [import-not-found] src/documents/barcodes.py:0: error: Cannot find implementation or library stub for module named "zxingcpp" [import-not-found]
src/documents/barcodes.py:0: error: Skipping analyzing "pyzbar": module is installed, but missing library stubs or py.typed marker [import-untyped]
src/documents/bulk_download.py:0: error: Return type "None" of "add_document" incompatible with return type "Never" in supertype "BulkArchiveStrategy" [override] src/documents/bulk_download.py:0: error: Return type "None" of "add_document" incompatible with return type "Never" in supertype "BulkArchiveStrategy" [override]
src/documents/bulk_download.py:0: error: Return type "None" of "add_document" incompatible with return type "Never" in supertype "BulkArchiveStrategy" [override] src/documents/bulk_download.py:0: error: Return type "None" of "add_document" incompatible with return type "Never" in supertype "BulkArchiveStrategy" [override]
src/documents/bulk_download.py:0: error: Return type "None" of "add_document" incompatible with return type "Never" in supertype "BulkArchiveStrategy" [override] src/documents/bulk_download.py:0: error: Return type "None" of "add_document" incompatible with return type "Never" in supertype "BulkArchiveStrategy" [override]

View File

@@ -154,8 +154,6 @@ ARG RUNTIME_PACKAGES="\
libmagic1 \ libmagic1 \
media-types \ media-types \
zlib1g \ zlib1g \
# Barcode splitter
libzbar0 \
poppler-utils" poppler-utils"
# Install basic runtime packages. # Install basic runtime packages.

View File

@@ -774,7 +774,6 @@ At this time, the library utilized for detection of barcodes supports the follow
- QR Code - QR Code
- SQ Code - SQ Code
You may check for updates on the [zbar library homepage](https://github.com/mchehab/zbar).
For usage in Paperless, the type of barcode does not matter, only the contents of it. For usage in Paperless, the type of barcode does not matter, only the contents of it.
For how to enable barcode usage, see [the configuration](configuration.md#barcodes). For how to enable barcode usage, see [the configuration](configuration.md#barcodes).

View File

@@ -1222,14 +1222,6 @@ using Python's `re.match()`, which anchors at the start of the filename.
The default ignores are `[.stfolder, .stversions, .localized, @eaDir, .Spotlight-V100, .Trashes, __MACOSX]` and cannot be overridden. The default ignores are `[.stfolder, .stversions, .localized, @eaDir, .Spotlight-V100, .Trashes, __MACOSX]` and cannot be overridden.
#### [`PAPERLESS_CONSUMER_BARCODE_SCANNER=<string>`](#PAPERLESS_CONSUMER_BARCODE_SCANNER) {#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.
#### [`PAPERLESS_PRE_CONSUME_SCRIPT=<filename>`](#PAPERLESS_PRE_CONSUME_SCRIPT) {#PAPERLESS_PRE_CONSUME_SCRIPT} #### [`PAPERLESS_PRE_CONSUME_SCRIPT=<filename>`](#PAPERLESS_PRE_CONSUME_SCRIPT) {#PAPERLESS_PRE_CONSUME_SCRIPT}
: After some initial validation, Paperless can trigger an arbitrary : After some initial validation, Paperless can trigger an arbitrary

View File

@@ -207,13 +207,12 @@ are released, dependency support is confirmed, etc.
- `libpq-dev` for PostgreSQL - `libpq-dev` for PostgreSQL
- `libmagic-dev` for mime type detection - `libmagic-dev` for mime type detection
- `mariadb-client` for MariaDB compile time - `mariadb-client` for MariaDB compile time
- `libzbar0` for barcode detection
- `poppler-utils` for barcode detection - `poppler-utils` for barcode detection
Use this list for your preferred package management: Use this list for your preferred package management:
``` ```
python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev pkg-config libmagic-dev libzbar0 poppler-utils python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev pkg-config libmagic-dev poppler-utils
``` ```
These dependencies are required for OCRmyPDF, which is used for text These dependencies are required for OCRmyPDF, which is used for text

View File

@@ -68,7 +68,6 @@ dependencies = [
"python-gnupg~=0.5.4", "python-gnupg~=0.5.4",
"python-ipware~=3.0.0", "python-ipware~=3.0.0",
"python-magic~=0.4.27", "python-magic~=0.4.27",
"pyzbar~=0.1.9",
"rapidfuzz~=3.14.0", "rapidfuzz~=3.14.0",
"redis[hiredis]~=5.2.1", "redis[hiredis]~=5.2.1",
"regex>=2025.9.18", "regex>=2025.9.18",

View File

@@ -28,8 +28,6 @@ from documents.utils import maybe_override_pixel_limit
from paperless.config import BarcodeConfig from paperless.config import BarcodeConfig
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Callable
from PIL import Image from PIL import Image
logger = logging.getLogger("paperless.barcodes") logger = logging.getLogger("paperless.barcodes")
@@ -262,26 +260,6 @@ class BarcodePlugin(ConsumeTaskPlugin):
return barcodes return barcodes
@staticmethod
def read_barcodes_pyzbar(image: Image.Image) -> list[str]:
barcodes = []
from pyzbar import pyzbar
# Decode the barcode image
detected_barcodes = pyzbar.decode(image)
# 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 {barcode.type} found: {decoded_barcode}",
)
return barcodes
def detect(self) -> None: def detect(self) -> None:
""" """
Scan all pages of the PDF as images, updating barcodes and the pages Scan all pages of the PDF as images, updating barcodes and the pages
@@ -294,14 +272,6 @@ class BarcodePlugin(ConsumeTaskPlugin):
# No op if not a TIFF # No op if not a TIFF
self.convert_from_tiff_to_pdf() self.convert_from_tiff_to_pdf()
# Choose the library for reading
if settings.CONSUMER_BARCODE_SCANNER == "PYZBAR":
reader: Callable[[Image.Image], list[str]] = self.read_barcodes_pyzbar
logger.debug("Scanning for barcodes using PYZBAR")
else:
reader = self.read_barcodes_zxing
logger.debug("Scanning for barcodes using ZXING")
try: try:
# Read number of pages from pdf # Read number of pages from pdf
with Pdf.open(self.pdf_file) as pdf: with Pdf.open(self.pdf_file) as pdf:
@@ -349,7 +319,7 @@ class BarcodePlugin(ConsumeTaskPlugin):
) )
# Detect barcodes # Detect barcodes
for barcode_value in reader(page): for barcode_value in self.read_barcodes_zxing(page):
self.barcodes.append( self.barcodes.append(
Barcode(current_page_number, barcode_value, self.settings), Barcode(current_page_number, barcode_value, self.settings),
) )

View File

@@ -4,7 +4,6 @@ from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
import pytest
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import TestCase
from django.test import override_settings from django.test import override_settings
@@ -25,13 +24,6 @@ from documents.tests.utils import FileSystemAssertsMixin
from documents.tests.utils import SampleDirMixin from documents.tests.utils import SampleDirMixin
from paperless.models import ApplicationConfiguration from paperless.models import ApplicationConfiguration
try:
import zxingcpp # noqa: F401
HAS_ZXING_LIB = True
except ImportError:
HAS_ZXING_LIB = False
class GetReaderPluginMixin: class GetReaderPluginMixin:
@contextmanager @contextmanager
@@ -48,7 +40,6 @@ class GetReaderPluginMixin:
reader.cleanup() reader.cleanup()
@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
class TestBarcode( class TestBarcode(
DirectoriesMixin, DirectoriesMixin,
FileSystemAssertsMixin, FileSystemAssertsMixin,
@@ -606,7 +597,6 @@ class TestBarcode(
self.assertDictEqual(separator_page_numbers, {0: False}) self.assertDictEqual(separator_page_numbers, {0: False})
@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
class TestBarcodeNewConsume( class TestBarcodeNewConsume(
DirectoriesMixin, DirectoriesMixin,
FileSystemAssertsMixin, FileSystemAssertsMixin,
@@ -784,14 +774,12 @@ class TestAsnBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, Tes
self.assertEqual(document.archive_serial_number, 123) self.assertEqual(document.archive_serial_number, 123)
@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
def test_scan_file_for_qrcode_without_upscale(self) -> None: def test_scan_file_for_qrcode_without_upscale(self) -> None:
""" """
GIVEN: GIVEN:
- A printed and scanned PDF document with a rather small QR code - A printed and scanned PDF document with a rather small QR code
WHEN: WHEN:
- ASN barcode detection is run with default settings - ASN barcode detection is run with default settings
- pyzbar is used for detection, as zxing would behave differently, and detect the QR code
THEN: THEN:
- ASN is not detected - ASN is not detected
""" """
@@ -802,7 +790,6 @@ class TestAsnBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, Tes
reader.detect() reader.detect()
self.assertEqual(len(reader.barcodes), 0) self.assertEqual(len(reader.barcodes), 0)
@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
@override_settings(CONSUMER_BARCODE_DPI=600) @override_settings(CONSUMER_BARCODE_DPI=600)
@override_settings(CONSUMER_BARCODE_UPSCALE=1.5) @override_settings(CONSUMER_BARCODE_UPSCALE=1.5)
def test_scan_file_for_qrcode_with_upscale(self) -> None: def test_scan_file_for_qrcode_with_upscale(self) -> None:
@@ -810,9 +797,7 @@ class TestAsnBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, Tes
GIVEN: GIVEN:
- A printed and scanned PDF document with a rather small QR code - A printed and scanned PDF document with a rather small QR code
WHEN: WHEN:
- ASN barcode detection is run with 600dpi and an upscale factor of 1.5 and pyzbar - ASN barcode detection is run with 600dpi and an upscale factor of 1.5
- pyzbar is used for detection, as zxing would behave differently.
Upscaling is a workaround for detection problems with pyzbar,
when you cannot switch to zxing (aarch64 build problems of zxing) when you cannot switch to zxing (aarch64 build problems of zxing)
THEN: THEN:
- ASN 123 is detected - ASN 123 is detected
@@ -826,24 +811,6 @@ class TestAsnBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, Tes
self.assertEqual(reader.asn, 123) self.assertEqual(reader.asn, 123)
@pytest.mark.skipif(
not HAS_ZXING_LIB,
reason="No zxingcpp",
)
@override_settings(CONSUMER_BARCODE_SCANNER="ZXING")
class TestBarcodeZxing(TestBarcode):
pass
@pytest.mark.skipif(
not HAS_ZXING_LIB,
reason="No zxingcpp",
)
@override_settings(CONSUMER_BARCODE_SCANNER="ZXING")
class TestAsnBarcodesZxing(TestAsnBarcode):
pass
class TestTagBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, TestCase): class TestTagBarcode(DirectoriesMixin, SampleDirMixin, GetReaderPluginMixin, TestCase):
@contextmanager @contextmanager
def get_reader(self, filepath: Path) -> BarcodePlugin: def get_reader(self, filepath: Path) -> BarcodePlugin:

View File

@@ -167,17 +167,6 @@ def settings_values_check(app_configs, **kwargs):
) )
return msgs return msgs
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
def _email_certificate_validate(): def _email_certificate_validate():
msgs = [] msgs = []
# Existence checks # Existence checks
@@ -195,7 +184,6 @@ def settings_values_check(app_configs, **kwargs):
return ( return (
_ocrmypdf_settings_check() _ocrmypdf_settings_check()
+ _timezone_validate() + _timezone_validate()
+ _barcode_scanner_validate()
+ _email_certificate_validate() + _email_certificate_validate()
) )

View File

@@ -1106,11 +1106,6 @@ CONSUMER_BARCODE_STRING: Final[str] = os.getenv(
"PATCHT", "PATCHT",
) )
CONSUMER_BARCODE_SCANNER: Final[str] = os.getenv(
"PAPERLESS_CONSUMER_BARCODE_SCANNER",
"PYZBAR",
).upper()
CONSUMER_ENABLE_ASN_BARCODE: Final[bool] = __get_boolean( CONSUMER_ENABLE_ASN_BARCODE: Final[bool] = __get_boolean(
"PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE", "PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE",
) )

View File

@@ -206,11 +206,6 @@ class TestBarcodeSettingsChecks(DirectoriesMixin, TestCase):
self.assertIn('Invalid Barcode Scanner ""', msg.msg) self.assertIn('Invalid Barcode Scanner ""', msg.msg)
@override_settings(CONSUMER_BARCODE_SCANNER="PYZBAR")
def test_barcode_scanner_valid(self) -> None:
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 0)
class TestEmailCertSettingsChecks(DirectoriesMixin, FileSystemAssertsMixin, TestCase): class TestEmailCertSettingsChecks(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
@override_settings(EMAIL_CERTIFICATE_FILE=Path("/tmp/not_actually_here.pem")) @override_settings(EMAIL_CERTIFICATE_FILE=Path("/tmp/not_actually_here.pem"))

10
uv.lock generated
View File

@@ -3073,7 +3073,6 @@ dependencies = [
{ name = "python-gnupg", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "python-gnupg", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "python-ipware", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "python-ipware", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "python-magic", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "python-magic", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "pyzbar", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "rapidfuzz", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "rapidfuzz", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "redis", extra = ["hiredis"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "redis", extra = ["hiredis"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "regex", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "regex", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
@@ -3226,7 +3225,6 @@ requires-dist = [
{ name = "python-gnupg", specifier = "~=0.5.4" }, { name = "python-gnupg", specifier = "~=0.5.4" },
{ name = "python-ipware", specifier = "~=3.0.0" }, { name = "python-ipware", specifier = "~=3.0.0" },
{ name = "python-magic", specifier = "~=0.4.27" }, { name = "python-magic", specifier = "~=0.4.27" },
{ name = "pyzbar", specifier = "~=0.1.9" },
{ name = "rapidfuzz", specifier = "~=3.14.0" }, { name = "rapidfuzz", specifier = "~=3.14.0" },
{ name = "redis", extras = ["hiredis"], specifier = "~=5.2.1" }, { name = "redis", extras = ["hiredis"], specifier = "~=5.2.1" },
{ name = "regex", specifier = ">=2025.9.18" }, { name = "regex", specifier = ">=2025.9.18" },
@@ -4279,14 +4277,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
] ]
[[package]]
name = "pyzbar"
version = "0.1.9"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6d/24/81ebe6a1c00760471a3028a23cbe0b94e5fa2926e5ba47adc895920887bc/pyzbar-0.1.9-py2.py3-none-any.whl", hash = "sha256:4559628b8192feb25766d954b36a3753baaf5c97c03135aec7e4a026036b475d", size = 32560, upload-time = "2022-03-15T14:53:40.637Z" },
]
[[package]] [[package]]
name = "qrcode" name = "qrcode"
version = "8.2" version = "8.2"