mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-19 23:11:59 +02:00
93 lines
3.8 KiB
Python
93 lines
3.8 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import functools
|
|
import re
|
|
from threading import RLock
|
|
|
|
__all__ = ['check_barcode_encoding', 'createBarcodeDrawing', 'get_barcode_font']
|
|
_barcode_init_lock = RLock()
|
|
|
|
|
|
# A lock occurs when the user wants to print a report having multiple barcode while the server is
|
|
# started in threaded-mode. The reason is that reportlab has to build a cache of the T1 fonts
|
|
# before rendering a barcode (done in a C extension) and this part is not thread safe.
|
|
# This cached functions allows to lazily initialize the T1 fonts cache need for rendering of
|
|
# barcodes in a thread-safe way.
|
|
@functools.lru_cache(1)
|
|
def _init_barcode():
|
|
with _barcode_init_lock:
|
|
try:
|
|
from reportlab.graphics import barcode # noqa: PLC0415
|
|
from reportlab.pdfbase.pdfmetrics import TypeFace, getFont # noqa: PLC0415
|
|
font_name = 'Courier'
|
|
available = TypeFace(font_name).findT1File()
|
|
if not available:
|
|
substitution_font = 'NimbusMonoPS-Regular'
|
|
fnt = getFont(substitution_font)
|
|
if fnt:
|
|
font_name = substitution_font
|
|
fnt.ascent = 629
|
|
fnt.descent = -157
|
|
barcode.createBarcodeDrawing('Code128', value='foo', format='png', width=100, height=100, humanReadable=1, fontName=font_name).asString('png')
|
|
except ImportError:
|
|
raise
|
|
except Exception: # noqa: BLE001
|
|
font_name = 'Courier'
|
|
return barcode, font_name
|
|
|
|
|
|
def createBarcodeDrawing(codeName: str, **options):
|
|
barcode, _font = _init_barcode()
|
|
return barcode.createBarcodeDrawing(codeName, **options)
|
|
|
|
|
|
def get_barcode_font():
|
|
"""Get the barcode font for rendering."""
|
|
_barcode, font = _init_barcode()
|
|
return font
|
|
|
|
|
|
def get_barcode_check_digit(numeric_barcode: str) -> int:
|
|
""" Computes and returns the barcode check digit. The used algorithm
|
|
follows the GTIN specifications and can be used by all compatible
|
|
barcode nomenclature, like as EAN-8, EAN-12 (UPC-A) or EAN-13.
|
|
https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
|
|
https://www.gs1.org/services/how-calculate-check-digit-manually
|
|
:param numeric_barcode: the barcode to verify/recompute the check digit
|
|
:return: the number corresponding to the right check digit
|
|
"""
|
|
# Multiply value of each position by
|
|
# N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 N11 N12 N13 N14 N15 N16 N17 N18
|
|
# x3 X1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 CHECKSUM
|
|
oddsum = evensum = 0
|
|
code = numeric_barcode[-2::-1] # Remove the check digit and reverse the barcode.
|
|
# The CHECKSUM digit is removed because it will be recomputed and it must not interfer with
|
|
# the computation. Also, the barcode is inverted, so the barcode length doesn't matter.
|
|
# Otherwise, the digits' group (even or odd) could be different according to the barcode length.
|
|
for i, digit in enumerate(code):
|
|
if i % 2 == 0:
|
|
evensum += int(digit)
|
|
else:
|
|
oddsum += int(digit)
|
|
total = evensum * 3 + oddsum
|
|
return (10 - total % 10) % 10
|
|
|
|
|
|
def check_barcode_encoding(barcode: str, encoding: str) -> bool:
|
|
""" Checks if the given barcode is correctly encoded.
|
|
:return: True if the barcode string is encoded with the provided encoding.
|
|
"""
|
|
encoding = encoding.lower()
|
|
if encoding == "any":
|
|
return True
|
|
barcode_sizes = {
|
|
'ean8': 8,
|
|
'ean13': 13,
|
|
'gtin14': 14,
|
|
'upca': 12,
|
|
'sscc': 18,
|
|
}
|
|
barcode_size = barcode_sizes[encoding]
|
|
return (encoding != 'ean13' or barcode[0] != '0') \
|
|
and len(barcode) == barcode_size \
|
|
and re.match(r"^\d+$", barcode) \
|
|
and get_barcode_check_digit(barcode) == int(barcode[-1])
|