Primer commit

This commit is contained in:
Mongar28
2024-02-03 21:52:43 -05:00
commit b5c17f85f1
3325 changed files with 855400 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,127 @@
"""This package provides a simple way to create standard barcodes.
It needs no external packages to be installed, the barcodes are
created as SVG objects. If Pillow is installed, the barcodes can also be
rendered as images (all formats supported by Pillow).
"""
import os
from typing import BinaryIO
from typing import Dict
from typing import Optional
from typing import Union
from barcode.codabar import CODABAR
from barcode.codex import PZN
from barcode.codex import Code39
from barcode.codex import Code128
from barcode.codex import Gs1_128
from barcode.ean import EAN8
from barcode.ean import EAN8_GUARD
from barcode.ean import EAN13
from barcode.ean import EAN13_GUARD
from barcode.ean import EAN14
from barcode.ean import JAN
from barcode.errors import BarcodeNotFoundError
from barcode.isxn import ISBN10
from barcode.isxn import ISBN13
from barcode.isxn import ISSN
from barcode.itf import ITF
from barcode.upc import UPCA
from barcode.version import version # noqa: F401
__BARCODE_MAP = {
"ean8": EAN8,
"ean8-guard": EAN8_GUARD,
"ean13": EAN13,
"ean13-guard": EAN13_GUARD,
"ean": EAN13,
"gtin": EAN14,
"ean14": EAN14,
"jan": JAN,
"upc": UPCA,
"upca": UPCA,
"isbn": ISBN13,
"isbn13": ISBN13,
"gs1": ISBN13,
"isbn10": ISBN10,
"issn": ISSN,
"code39": Code39,
"pzn": PZN,
"code128": Code128,
"itf": ITF,
"gs1_128": Gs1_128,
"codabar": CODABAR,
"nw-7": CODABAR,
}
PROVIDED_BARCODES = list(__BARCODE_MAP)
PROVIDED_BARCODES.sort()
def get(
name: str,
code: Optional[str] = None,
writer=None,
options: Optional[dict] = None,
):
"""Helper method for getting a generator or even a generated code.
:param name: The name of the type of barcode desired.
:param code: The actual information to encode. If this parameter is
provided, a generated barcode is returned. Otherwise, the barcode class
is returned.
:param Writer writer: An alternative writer to use when generating the
barcode.
:param options: Additional options to be passed on to the barcode when
generating.
"""
options = options or {}
try:
barcode = __BARCODE_MAP[name.lower()]
except KeyError as e:
raise BarcodeNotFoundError(f"The barcode {name!r} is not known.") from e
if code is not None:
return barcode(code, writer, **options)
return barcode
def get_class(name):
return get_barcode(name)
def generate(
name: str,
code: str,
writer=None,
output: Union[str, os.PathLike, BinaryIO, None] = None,
writer_options: Union[Dict, None] = None,
text: Union[str, None] = None,
):
"""Shortcut to generate a barcode in one line.
:param name: Name of the type of barcode to use.
:param code: Data to encode into the barcode.
:param writer: A writer to use (e.g.: ImageWriter or SVGWriter).
:param output: Destination file-like or path-like where to save the generated
barcode.
:param writer_options: Options to pass on to the writer instance.
:param text: Text to render under the barcode.
"""
from barcode.base import Barcode
writer = writer or Barcode.default_writer()
writer.set_options(writer_options or {})
barcode = get(name, code, writer)
if isinstance(output, str):
return barcode.save(output, writer_options, text)
if output:
barcode.write(output, writer_options, text)
return None
raise TypeError("'output' cannot be None")
get_barcode = get
get_barcode_class = get_class

View File

@@ -0,0 +1,100 @@
"""barcode.base
"""
from typing import ClassVar
from typing import List
from typing import Optional
from barcode.writer import BaseWriter
from barcode.writer import SVGWriter
class Barcode:
name = ""
digits = 0
default_writer = SVGWriter
default_writer_options: ClassVar[dict] = {
"module_width": 0.2,
"module_height": 15.0,
"quiet_zone": 6.5,
"font_size": 10,
"text_distance": 5.0,
"background": "white",
"foreground": "black",
"write_text": True,
"text": "",
}
writer: BaseWriter
def to_ascii(self) -> str:
code = self.build()
for i, line in enumerate(code):
code[i] = line.replace("1", "X").replace("0", " ")
return "\n".join(code)
def __repr__(self) -> str:
return f"<{self.__class__.__name__}({self.get_fullcode()!r})>"
def build(self) -> List[str]:
raise NotImplementedError
def get_fullcode(self):
"""Returns the full code, encoded in the barcode.
:returns: Full human readable code.
:rtype: String
"""
raise NotImplementedError
def save(
self, filename: str, options: Optional[dict] = None, text: Optional[str] = None
) -> str:
"""Renders the barcode and saves it in `filename`.
:param filename: Filename to save the barcode in (without filename extension).
:param options: The same as in `self.render`.
:param text: Text to render under the barcode.
:returns: The full filename with extension.
"""
output = self.render(options, text) if text else self.render(options)
return self.writer.save(filename, output)
def write(self, fp, options=None, text=None):
"""Renders the barcode and writes it to the file like object
`fp`.
:parameters:
fp : File like object
Object to write the raw data in.
options : Dict
The same as in `self.render`.
text : str
Text to render under the barcode.
"""
output = self.render(options, text)
self.writer.write(output, fp)
def render(self, writer_options: Optional[dict] = None, text: Optional[str] = None):
"""Renders the barcode using `self.writer`.
:param writer_options: Options for `self.writer`, see writer docs for details.
:param text: Text to render under the barcode.
:returns: Output of the writers render method.
"""
options = self.default_writer_options.copy()
options.update(writer_options or {})
if options["write_text"] or text is not None:
if text is not None:
options["text"] = text
else:
options["text"] = self.get_fullcode()
self.writer.set_options(options)
code = self.build()
return self.writer.render(code)

View File

@@ -0,0 +1,25 @@
# W = Wide bar
# w = wide space
# N = Narrow bar
# n = narrow space
CODES = {
"0": "NnNnNwW",
"1": "NnNnWwN",
"2": "NnNwNnW",
"3": "WwNnNnN",
"4": "NnWnNwN",
"5": "WnNnNwN",
"6": "NwNnNnW",
"7": "NwNnWnN",
"8": "NwWnNnN",
"9": "WnNwNnN",
"-": "NnNwWnN",
"$": "NnWwNnN",
":": "WnNnWnW",
"/": "WnWnNnW",
".": "WnWnWnN",
"+": "NnWnWnW",
}
STARTSTOP = {"A": "NnWwNwN", "B": "NwNwNnW", "C": "NnNwNwW", "D": "NnNwWwN"}

View File

@@ -0,0 +1,216 @@
import string
# Charsets for code 128
_common = (
" ",
"!",
'"',
"#",
"$",
"%",
"&",
"'",
"(",
")",
"*",
"+",
",",
"-",
".",
"/",
*tuple(string.digits),
":",
";",
"<",
"=",
">",
"?",
"@",
*tuple(string.ascii_uppercase),
"[",
"\\",
"]",
"^",
"_",
)
_charset_a = (
*_common,
"\x00",
"\x01",
"\x02",
"\x03",
"\x04",
"\x05",
"\x06",
"\x07",
"\x08",
"\t",
"\n",
"\x0b",
"\x0c",
"\r",
"\x0e",
"\x0f",
"\x10",
"\x11",
"\x12",
"\x13",
"\x14",
"\x15",
"\x16",
"\x17",
"\x18",
"\x19",
"\x1a",
"\x1b",
"\x1c",
"\x1d",
"\x1e",
"\x1f",
"ó",
"ò",
"SHIFT",
"TO_C",
"TO_B",
"ô",
"ñ",
)
_charset_b = (
*_common,
"`",
*tuple(string.ascii_lowercase),
"{",
"|",
"}",
"~",
"\x7f",
"ó",
"ò",
"SHIFT",
"TO_C",
"ô",
"TO_A",
"ñ",
)
ALL = set(_common + _charset_a + _charset_b)
A = {c: i for i, c in enumerate(_charset_a)}
B = {c: i for i, c in enumerate(_charset_b)}
C = {"TO_B": 100, "TO_A": 101, "\xf1": 102}
CODES = (
"11011001100",
"11001101100",
"11001100110",
"10010011000",
"10010001100",
"10001001100",
"10011001000",
"10011000100",
"10001100100",
"11001001000",
"11001000100",
"11000100100",
"10110011100",
"10011011100",
"10011001110",
"10111001100",
"10011101100",
"10011100110",
"11001110010",
"11001011100",
"11001001110",
"11011100100",
"11001110100",
"11101101110",
"11101001100",
"11100101100",
"11100100110",
"11101100100",
"11100110100",
"11100110010",
"11011011000",
"11011000110",
"11000110110",
"10100011000",
"10001011000",
"10001000110",
"10110001000",
"10001101000",
"10001100010",
"11010001000",
"11000101000",
"11000100010",
"10110111000",
"10110001110",
"10001101110",
"10111011000",
"10111000110",
"10001110110",
"11101110110",
"11010001110",
"11000101110",
"11011101000",
"11011100010",
"11011101110",
"11101011000",
"11101000110",
"11100010110",
"11101101000",
"11101100010",
"11100011010",
"11101111010",
"11001000010",
"11110001010",
"10100110000",
"10100001100",
"10010110000",
"10010000110",
"10000101100",
"10000100110",
"10110010000",
"10110000100",
"10011010000",
"10011000010",
"10000110100",
"10000110010",
"11000010010",
"11001010000",
"11110111010",
"11000010100",
"10001111010",
"10100111100",
"10010111100",
"10010011110",
"10111100100",
"10011110100",
"10011110010",
"11110100100",
"11110010100",
"11110010010",
"11011011110",
"11011110110",
"11110110110",
"10101111000",
"10100011110",
"10001011110",
"10111101000",
"10111100010",
"11110101000",
"11110100010",
"10111011110",
"10111101110",
"11101011110",
"11110101110",
"11010000100",
"11010010000",
"11010011100",
)
STOP = "11000111010"
START_CODES = {"A": 103, "B": 104, "C": 105}
TO = {101: START_CODES["A"], 100: START_CODES["B"], 99: START_CODES["C"]}

View File

@@ -0,0 +1,61 @@
import string
# Charsets for code 39
REF = (
tuple(string.digits)
+ tuple(string.ascii_uppercase)
+ ("-", ".", " ", "$", "/", "+", "%")
)
B = "1"
E = "0"
CODES = (
"101000111011101",
"111010001010111",
"101110001010111",
"111011100010101",
"101000111010111",
"111010001110101",
"101110001110101",
"101000101110111",
"111010001011101",
"101110001011101",
"111010100010111",
"101110100010111",
"111011101000101",
"101011100010111",
"111010111000101",
"101110111000101",
"101010001110111",
"111010100011101",
"101110100011101",
"101011100011101",
"111010101000111",
"101110101000111",
"111011101010001",
"101011101000111",
"111010111010001",
"101110111010001",
"101010111000111",
"111010101110001",
"101110101110001",
"101011101110001",
"111000101010111",
"100011101010111",
"111000111010101",
"100010111010111",
"111000101110101",
"100011101110101",
"100010101110111",
"111000101011101",
"100011101011101",
"100010001000101",
"100010001010001",
"100010100010001",
"101000100010001",
)
EDGE = "100010111011101"
MIDDLE = "0"
# MAP for assigning every symbol (REF) to (reference number, barcode)
MAP = dict(zip(REF, enumerate(CODES)))

View File

@@ -0,0 +1,52 @@
EDGE = "101"
MIDDLE = "01010"
CODES = {
"A": (
"0001101",
"0011001",
"0010011",
"0111101",
"0100011",
"0110001",
"0101111",
"0111011",
"0110111",
"0001011",
),
"B": (
"0100111",
"0110011",
"0011011",
"0100001",
"0011101",
"0111001",
"0000101",
"0010001",
"0001001",
"0010111",
),
"C": (
"1110010",
"1100110",
"1101100",
"1000010",
"1011100",
"1001110",
"1010000",
"1000100",
"1001000",
"1110100",
),
}
LEFT_PATTERN = (
"AAAAAA",
"AABABB",
"AABBAB",
"AABBBA",
"ABAABB",
"ABBAAB",
"ABBBAA",
"ABABAB",
"ABABBA",
"ABBABA",
)

View File

@@ -0,0 +1,19 @@
# W = Wide bar
# w = wide space
# N = Narrow bar
# n = narrow space
START = "NnNn"
STOP = "WnN"
CODES = (
"NNWWN",
"WNNNW",
"NWNNW",
"WWNNN",
"NNWNW",
"WNWNN",
"NWWNN",
"NNNWW",
"WNNWN",
"NWNWN",
)

View File

@@ -0,0 +1,28 @@
EDGE = "101"
MIDDLE = "01010"
CODES = {
"L": (
"0001101",
"0011001",
"0010011",
"0111101",
"0100011",
"0110001",
"0101111",
"0111011",
"0110111",
"0001011",
),
"R": (
"1110010",
"1100110",
"1101100",
"1000010",
"1011100",
"1001110",
"1010000",
"1000100",
"1001000",
"1110100",
),
}

View File

@@ -0,0 +1,75 @@
"""Module: barcode.codabar
:Provided barcodes: Codabar (NW-7)
"""
__docformat__ = "restructuredtext en"
from barcode.base import Barcode
from barcode.charsets import codabar
from barcode.errors import BarcodeError
from barcode.errors import IllegalCharacterError
class CODABAR(Barcode):
"""Initializes a new CODABAR instance.
:parameters:
code : String
Codabar (NW-7) string that matches [ABCD][0-9$:/.+-]+[ABCD]
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
narrow: Integer
Width of the narrow elements (default: 2)
wide: Integer
Width of the wide elements (default: 5)
wide/narrow must be in the range 2..3
"""
name = "Codabar (NW-7)"
def __init__(self, code, writer=None, narrow=2, wide=5) -> None:
self.code = code
self.writer = writer or self.default_writer()
self.narrow = narrow
self.wide = wide
def __str__(self) -> str:
return self.code
def get_fullcode(self):
return self.code
def build(self):
try:
data = (
codabar.STARTSTOP[self.code[0]] + "n"
) # Start with [A-D], followed by a narrow space
except KeyError:
raise BarcodeError("Codabar should start with either A,B,C or D") from None
try:
data += "n".join(
[codabar.CODES[c] for c in self.code[1:-1]]
) # separated by a narrow space
except KeyError:
raise IllegalCharacterError(
"Codabar can only contain numerics or $:/.+-"
) from None
try:
data += "n" + codabar.STARTSTOP[self.code[-1]] # End with [A-D]
except KeyError:
raise BarcodeError("Codabar should end with either A,B,C or D") from None
raw = ""
for e in data:
if e == "W":
raw += "1" * self.wide
if e == "w":
raw += "0" * self.wide
if e == "N":
raw += "1" * self.narrow
if e == "n":
raw += "0" * self.narrow
return [raw]

View File

@@ -0,0 +1,279 @@
"""Module: barcode.codex
:Provided barcodes: Code 39, Code 128, PZN
"""
from typing import Collection
from barcode.base import Barcode
from barcode.charsets import code39
from barcode.charsets import code128
from barcode.errors import BarcodeError
from barcode.errors import IllegalCharacterError
from barcode.errors import NumberOfDigitsError
__docformat__ = "restructuredtext en"
# Sizes
MIN_SIZE = 0.2
MIN_QUIET_ZONE = 2.54
def check_code(code: str, name: str, allowed: Collection[str]) -> None:
wrong = []
for char in code:
if char not in allowed:
wrong.append(char)
if wrong:
raise IllegalCharacterError(
"The following characters are not valid for {name}: {wrong}".format(
name=name, wrong=", ".join(wrong)
)
)
class Code39(Barcode):
"""A Code39 barcode implementation"""
name = "Code 39"
def __init__(self, code: str, writer=None, add_checksum: bool = True) -> None:
r"""
:param code: Code 39 string without \* and without checksum.
:param writer: A ``barcode.writer`` instance used to render the barcode
(default: SVGWriter).
:param add_checksum: Add the checksum to code or not
"""
self.code = code.upper()
if add_checksum:
self.code += self.calculate_checksum()
self.writer = writer or self.default_writer()
check_code(self.code, self.name, code39.REF)
def __str__(self) -> str:
return self.code
def get_fullcode(self) -> str:
""":returns: The full code as it will be encoded."""
return self.code
def calculate_checksum(self):
check = sum(code39.MAP[x][0] for x in self.code) % 43
for k, v in code39.MAP.items():
if check == v[0]:
return k
return None
def build(self):
chars = [code39.EDGE]
for char in self.code:
chars.append(code39.MAP[char][1])
chars.append(code39.EDGE)
return [code39.MIDDLE.join(chars)]
def render(self, writer_options=None, text=None):
options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE}
options.update(writer_options or {})
return super().render(options, text)
class PZN7(Code39):
"""Initializes new German number for pharmaceutical products.
:parameters:
pzn : String
Code to render.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "Pharmazentralnummer"
digits = 6
def __init__(self, pzn, writer=None) -> None:
pzn = pzn[: self.digits]
if not pzn.isdigit():
raise IllegalCharacterError("PZN can only contain numbers.")
if len(pzn) != self.digits:
raise NumberOfDigitsError(
f"PZN must have {self.digits} digits, not {len(pzn)}."
)
self.pzn = pzn
self.pzn = f"{pzn}{self.calculate_checksum()}"
super().__init__(f"PZN-{self.pzn}", writer, add_checksum=False)
def get_fullcode(self):
return f"PZN-{self.pzn}"
def calculate_checksum(self):
sum_ = sum(int(x) * int(y) for x, y in enumerate(self.pzn, start=2))
checksum = sum_ % 11
if checksum == 10:
raise BarcodeError("Checksum can not be 10 for PZN.")
return checksum
class PZN8(PZN7):
"""Will be fully added in v0.9."""
digits = 7
class Code128(Barcode):
"""Initializes a new Code128 instance. The checksum is added automatically
when building the bars.
:parameters:
code : String
Code 128 string without checksum (added automatically).
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "Code 128"
def __init__(self, code, writer=None) -> None:
self.code = code
self.writer = writer or self.default_writer()
self._charset = "B"
self._buffer = ""
check_code(self.code, self.name, code128.ALL)
def __str__(self) -> str:
return self.code
@property
def encoded(self):
return self._build()
def get_fullcode(self):
return self.code
def _new_charset(self, which):
if which == "A":
code = self._convert("TO_A")
elif which == "B":
code = self._convert("TO_B")
elif which == "C":
code = self._convert("TO_C")
self._charset = which
return [code]
def _maybe_switch_charset(self, pos):
char = self.code[pos]
next_ = self.code[pos : pos + 10]
def look_next():
digits = 0
for c in next_:
if c.isdigit():
digits += 1
else:
break
return digits > 3
codes = []
if self._charset == "C" and not char.isdigit():
if char in code128.B:
codes = self._new_charset("B")
elif char in code128.A:
codes = self._new_charset("A")
if len(self._buffer) == 1:
codes.append(self._convert(self._buffer[0]))
self._buffer = ""
elif self._charset == "B":
if look_next():
codes = self._new_charset("C")
elif char not in code128.B and char in code128.A:
codes = self._new_charset("A")
elif self._charset == "A":
if look_next():
codes = self._new_charset("C")
elif char not in code128.A and char in code128.B:
codes = self._new_charset("B")
return codes
def _convert(self, char):
if self._charset == "A":
return code128.A[char]
if self._charset == "B":
return code128.B[char]
if self._charset == "C":
if char in code128.C:
return code128.C[char]
if char.isdigit():
self._buffer += char
if len(self._buffer) == 2:
value = int(self._buffer)
self._buffer = ""
return value
return None
return None
return None
def _try_to_optimize(self, encoded):
if encoded[1] in code128.TO:
encoded[:2] = [code128.TO[encoded[1]]]
return encoded
def _calculate_checksum(self, encoded):
cs = [encoded[0]]
for i, code_num in enumerate(encoded[1:], start=1):
cs.append(i * code_num)
return sum(cs) % 103
def _build(self):
encoded = [code128.START_CODES[self._charset]]
for i, char in enumerate(self.code):
encoded.extend(self._maybe_switch_charset(i))
code_num = self._convert(char)
if code_num is not None:
encoded.append(code_num)
# Finally look in the buffer
if len(self._buffer) == 1:
encoded.extend(self._new_charset("B"))
encoded.append(self._convert(self._buffer[0]))
self._buffer = ""
return self._try_to_optimize(encoded)
def build(self):
encoded = self._build()
encoded.append(self._calculate_checksum(encoded))
code = ""
for code_num in encoded:
code += code128.CODES[code_num]
code += code128.STOP
code += "11"
return [code]
def render(self, writer_options=None, text=None):
options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE}
options.update(writer_options or {})
return super().render(options, text)
class Gs1_128(Code128): # noqa: N801
"""
following the norm, a gs1-128 barcode is a subset of code 128 barcode,
it can be generated by prepending the code with the FNC1 character
https://en.wikipedia.org/wiki/GS1-128
https://www.gs1-128.info/
"""
name = "GS1-128"
FNC1_CHAR = "\xf1"
def __init__(self, code, writer=None) -> None:
code = self.FNC1_CHAR + code
super().__init__(code, writer)
def get_fullcode(self):
return super().get_fullcode()[1:]
# For pre 0.8 compatibility
PZN = PZN7

View File

@@ -0,0 +1,234 @@
"""Module: barcode.ean
:Provided barcodes: EAN-14, EAN-13, EAN-8, JAN
"""
__docformat__ = "restructuredtext en"
from functools import reduce
from barcode.base import Barcode
from barcode.charsets import ean as _ean
from barcode.errors import IllegalCharacterError
from barcode.errors import NumberOfDigitsError
from barcode.errors import WrongCountryCodeError
# EAN13 Specs (all sizes in mm)
SIZES = {
"SC0": 0.27,
"SC1": 0.297,
"SC2": 0.33,
"SC3": 0.363,
"SC4": 0.396,
"SC5": 0.445,
"SC6": 0.495,
"SC7": 0.544,
"SC8": 0.61,
"SC9": 0.66,
}
class EuropeanArticleNumber13(Barcode):
"""Initializes EAN13 object.
:parameters:
ean : String
The ean number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "EAN-13"
digits = 12
def __init__(self, ean, writer=None, no_checksum=False, guardbar=False) -> None:
ean = ean[: self.digits]
if not ean.isdigit():
raise IllegalCharacterError("EAN code can only contain numbers.")
if len(ean) != self.digits:
raise NumberOfDigitsError(
"EAN must have {} digits, not {}.".format(
self.digits,
len(ean),
)
)
self.ean = ean
# If no checksum
if no_checksum:
# Add a thirteen char if given in parameter,
# otherwise pad with zero
self.ean = "{}{}".format(
ean, ean[self.digits] if len(ean) > self.digits else 0
)
else:
self.ean = f"{ean}{self.calculate_checksum()}"
self.guardbar = guardbar
if guardbar:
self.EDGE = _ean.EDGE.replace("1", "G")
self.MIDDLE = _ean.MIDDLE.replace("1", "G")
else:
self.EDGE = _ean.EDGE
self.MIDDLE = _ean.MIDDLE
self.writer = writer or self.default_writer()
def __str__(self) -> str:
return self.ean
def get_fullcode(self):
if self.guardbar:
return self.ean[0] + " " + self.ean[1:7] + " " + self.ean[7:] + " >"
return self.ean
def calculate_checksum(self):
"""Calculates the checksum for EAN13-Code.
:returns: The checksum for `self.ean`.
:rtype: Integer
"""
def sum_(x, y):
return int(x) + int(y)
evensum = reduce(sum_, self.ean[-2::-2])
oddsum = reduce(sum_, self.ean[-1::-2])
return (10 - ((evensum + oddsum * 3) % 10)) % 10
def build(self):
"""Builds the barcode pattern from `self.ean`.
:returns: The pattern as string
:rtype: String
"""
code = self.EDGE[:]
pattern = _ean.LEFT_PATTERN[int(self.ean[0])]
for i, number in enumerate(self.ean[1:7]):
code += _ean.CODES[pattern[i]][int(number)]
code += self.MIDDLE
for number in self.ean[7:]:
code += _ean.CODES["C"][int(number)]
code += self.EDGE
return [code]
def to_ascii(self):
"""Returns an ascii representation of the barcode.
:rtype: String
"""
code = self.build()
for i, line in enumerate(code):
code[i] = line.replace("G", "|").replace("1", "|").replace("0", " ")
return "\n".join(code)
def render(self, writer_options=None, text=None):
options = {"module_width": SIZES["SC2"]}
options.update(writer_options or {})
return super().render(options, text)
class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13):
name = "EAN-13 with guards"
def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None:
super().__init__(ean, writer, no_checksum, guardbar)
class JapanArticleNumber(EuropeanArticleNumber13):
"""Initializes JAN barcode.
:parameters:
jan : String
The jan number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "JAN"
valid_country_codes = list(range(450, 460)) + list(range(490, 500))
def __init__(self, jan, *args, **kwargs) -> None:
if int(jan[:3]) not in self.valid_country_codes:
raise WrongCountryCodeError(
"Country code isn't between 450-460 or 490-500."
)
super().__init__(jan, *args, **kwargs)
class EuropeanArticleNumber8(EuropeanArticleNumber13):
"""Represents an EAN-8 barcode. See EAN13's __init__ for details.
:parameters:
ean : String
The ean number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "EAN-8"
digits = 7
def build(self):
"""Builds the barcode pattern from `self.ean`.
:returns: The pattern as string
:rtype: String
"""
code = self.EDGE[:]
for number in self.ean[:4]:
code += _ean.CODES["A"][int(number)]
code += self.MIDDLE
for number in self.ean[4:]:
code += _ean.CODES["C"][int(number)]
code += self.EDGE
return [code]
def get_fullcode(self):
if self.guardbar:
return "< " + self.ean[:4] + " " + self.ean[4:] + " >"
return self.ean
class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8):
name = "EAN-8 with guards"
def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None:
super().__init__(ean, writer, no_checksum, guardbar)
class EuropeanArticleNumber14(EuropeanArticleNumber13):
"""Represents an EAN-14 barcode. See EAN13's __init__ for details.
:parameters:
ean : String
The ean number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "EAN-14"
digits = 13
def calculate_checksum(self):
"""Calculates the checksum for EAN13-Code.
:returns: The checksum for `self.ean`.
:rtype: Integer
"""
def sum_(x, y):
return int(x) + int(y)
evensum = reduce(sum_, self.ean[::2])
oddsum = reduce(sum_, self.ean[1::2])
return (10 - (((evensum * 3) + oddsum) % 10)) % 10
# Shortcuts
EAN14 = EuropeanArticleNumber14
EAN13 = EuropeanArticleNumber13
EAN13_GUARD = EuropeanArticleNumber13WithGuard
EAN8 = EuropeanArticleNumber8
EAN8_GUARD = EuropeanArticleNumber8WithGuard
JAN = JapanArticleNumber

View File

@@ -0,0 +1,28 @@
"""barcode.errors"""
__docformat__ = "restructuredtext en"
class BarcodeError(Exception):
def __init__(self, msg) -> None:
self.msg = msg
def __str__(self) -> str:
return self.msg
class IllegalCharacterError(BarcodeError):
"""Raised when a barcode-string contains illegal characters."""
class BarcodeNotFoundError(BarcodeError):
"""Raised when an unknown barcode is requested."""
class NumberOfDigitsError(BarcodeError):
"""Raised when the number of digits do not match."""
class WrongCountryCodeError(BarcodeError):
"""Raised when a JAN (Japan Article Number) don't starts with 450-459
or 490-499.
"""

View File

@@ -0,0 +1,129 @@
"""Module: barcode.isxn
:Provided barcodes: ISBN-13, ISBN-10, ISSN
This module provides some special codes, which are no standalone barcodes.
All codes where transformed to EAN-13 barcodes. In every case, the checksum
is new calculated.
Example::
>>> from barcode import get_barcode
>>> ISBN = get_barcode('isbn10')
>>> isbn = ISBN('0132354187')
>>> isbn
'0132354187'
>>> isbn.get_fullcode()
'9780132354189'
>>> # Test with wrong checksum
>>> isbn = ISBN('0132354180')
>>> isbn
'0132354187'
"""
from barcode.ean import EuropeanArticleNumber13
from barcode.errors import BarcodeError
from barcode.errors import WrongCountryCodeError
__docformat__ = "restructuredtext en"
class InternationalStandardBookNumber13(EuropeanArticleNumber13):
"""Initializes new ISBN-13 barcode.
:parameters:
isbn : String
The isbn number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "ISBN-13"
def __init__(self, isbn, writer=None) -> None:
isbn = isbn.replace("-", "")
self.isbn13 = isbn
if isbn[:3] not in ("978", "979"):
raise WrongCountryCodeError("ISBN must start with 978 or 979.")
if isbn[:3] == "979" and isbn[3:4] not in ("1", "8"):
raise BarcodeError("ISBN must start with 97910 or 97911.")
super().__init__(isbn, writer)
class InternationalStandardBookNumber10(InternationalStandardBookNumber13):
"""Initializes new ISBN-10 barcode. This code is rendered as EAN-13 by
prefixing it with 978.
:parameters:
isbn : String
The isbn number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "ISBN-10"
digits = 9
def __init__(self, isbn, writer=None) -> None:
isbn = isbn.replace("-", "")
isbn = isbn[: self.digits]
super().__init__("978" + isbn, writer)
self.isbn10 = isbn
self.isbn10 = f"{isbn}{self._calculate_checksum()}"
def _calculate_checksum(self):
tmp = sum(x * int(y) for x, y in enumerate(self.isbn10[:9], start=1)) % 11
if tmp == 10:
return "X"
return tmp
def __str__(self) -> str:
return self.isbn10
class InternationalStandardSerialNumber(EuropeanArticleNumber13):
"""Initializes new ISSN barcode. This code is rendered as EAN-13
by prefixing it with 977 and adding 00 between code and checksum.
:parameters:
issn : String
The issn number as string.
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
"""
name = "ISSN"
digits = 7
def __init__(self, issn, writer=None) -> None:
issn = issn.replace("-", "")
issn = issn[: self.digits]
self.issn = issn
self.issn = f"{issn}{self._calculate_checksum()}"
super().__init__(self.make_ean(), writer)
def _calculate_checksum(self):
tmp = (
11
- sum(x * int(y) for x, y in enumerate(reversed(self.issn[:7]), start=2))
% 11
)
if tmp == 10:
return "X"
return tmp
def make_ean(self):
return f"977{self.issn[:7]}00{self._calculate_checksum()}"
def __str__(self) -> str:
return self.issn
# Shortcuts
ISBN13 = InternationalStandardBookNumber13
ISBN10 = InternationalStandardBookNumber10
ISSN = InternationalStandardSerialNumber

View File

@@ -0,0 +1,76 @@
"""Module: barcode.itf
:Provided barcodes: Interleaved 2 of 5
"""
__docformat__ = "restructuredtext en"
from barcode.base import Barcode
from barcode.charsets import itf
from barcode.errors import IllegalCharacterError
MIN_SIZE = 0.2
MIN_QUIET_ZONE = 6.4
class ITF(Barcode):
"""Initializes a new ITF instance.
:parameters:
code : String
ITF (Interleaved 2 of 5) numeric string
writer : barcode.writer Instance
The writer to render the barcode (default: SVGWriter).
narrow: Integer
Width of the narrow elements (default: 2)
wide: Integer
Width of the wide elements (default: 5)
wide/narrow must be in the range 2..3
"""
name = "ITF"
def __init__(self, code, writer=None, narrow=2, wide=5) -> None:
if not code.isdigit():
raise IllegalCharacterError("ITF code can only contain numbers.")
# Length must be even, prepend 0 if necessary
if len(code) % 2 != 0:
code = "0" + code
self.code = code
self.writer = writer or self.default_writer()
self.narrow = narrow
self.wide = wide
def __str__(self) -> str:
return self.code
def get_fullcode(self):
return self.code
def build(self):
data = itf.START
for i in range(0, len(self.code), 2):
bars_digit = int(self.code[i])
spaces_digit = int(self.code[i + 1])
for j in range(5):
data += itf.CODES[bars_digit][j].upper()
data += itf.CODES[spaces_digit][j].lower()
data += itf.STOP
raw = ""
for e in data:
if e == "W":
raw += "1" * self.wide
if e == "w":
raw += "0" * self.wide
if e == "N":
raw += "1" * self.narrow
if e == "n":
raw += "0" * self.narrow
return [raw]
def render(self, writer_options, text=None):
options = {
"module_width": MIN_SIZE / self.narrow,
"quiet_zone": MIN_QUIET_ZONE,
}
options.update(writer_options or {})
return super().render(options, text)

View File

@@ -0,0 +1,105 @@
import os
from argparse import ArgumentParser
import barcode
from barcode.version import version
from barcode.writer import ImageWriter
from barcode.writer import SVGWriter
IMG_FORMATS = ("BMP", "GIF", "JPEG", "MSP", "PCX", "PNG", "TIFF", "XBM")
def list_types(args, parser=None):
print("\npython-barcode available barcode formats:")
print(", ".join(barcode.PROVIDED_BARCODES))
print("\n")
print("Available image formats")
print("Standard: svg")
if ImageWriter is not None:
print("Pillow:", ", ".join(IMG_FORMATS))
else:
print("Pillow: disabled")
print("\n")
def create_barcode(args, parser):
args.type = args.type.upper()
if args.type != "SVG" and args.type not in IMG_FORMATS:
parser.error(
"Unknown type {type}. Try list action for available types.".format(
type=args.type
)
)
args.barcode = args.barcode.lower()
if args.barcode not in barcode.PROVIDED_BARCODES:
parser.error(
"Unknown barcode {bc}. Try list action for available barcodes.".format(
bc=args.barcode
)
)
if args.type != "SVG":
opts = {"format": args.type}
writer = ImageWriter()
else:
opts = {"compress": args.compress}
writer = SVGWriter()
out = os.path.normpath(os.path.abspath(args.output))
name = barcode.generate(args.barcode, args.code, writer, out, opts, args.text)
print(f"New barcode saved as {name}.")
def main():
msg = []
if ImageWriter is None:
msg.append("Image output disabled (Pillow not found), --type option disabled.")
else:
msg.append(
"Image output enabled, use --type option to give image "
"format (png, jpeg, ...)."
)
parser = ArgumentParser(
description="Create standard barcodes via cli.", epilog=" ".join(msg)
)
parser.add_argument(
"-v", "--version", action="version", version="%(prog)s " + version
)
subparsers = parser.add_subparsers(title="Actions")
create_parser = subparsers.add_parser(
"create", help="Create a barcode with the given options."
)
create_parser.add_argument("code", help="Code to render as barcode.")
create_parser.add_argument(
"output", help="Filename for output without extension, e. g. mybarcode."
)
create_parser.add_argument(
"-c",
"--compress",
action="store_true",
help="Compress output, only recognized if type is svg.",
)
create_parser.add_argument(
"-b", "--barcode", help="Barcode to use [default: %(default)s]."
)
create_parser.add_argument("--text", help="Text to show under the barcode.")
if ImageWriter is not None:
create_parser.add_argument(
"-t", "--type", help="Type of output [default: %(default)s]."
)
list_parser = subparsers.add_parser(
"list", help="List available image and code types."
)
list_parser.set_defaults(func=list_types)
create_parser.set_defaults(
type="svg", compress=False, func=create_barcode, barcode="code39", text=None
)
args = parser.parse_args()
try:
func = args.func
except AttributeError:
parser.error("You need to tell me what to do.")
else:
func(args, parser)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,115 @@
"""Module: barcode.upc
:Provided barcodes: UPC-A
"""
__docformat__ = "restructuredtext en"
from functools import reduce
from barcode.base import Barcode
from barcode.charsets import upc as _upc
from barcode.errors import IllegalCharacterError
from barcode.errors import NumberOfDigitsError
class UniversalProductCodeA(Barcode):
"""Universal Product Code (UPC) barcode.
UPC-A consists of 12 numeric digits.
"""
name = "UPC-A"
digits = 11
def __init__(self, upc, writer=None, make_ean=False) -> None:
"""Initializes new UPC-A barcode.
:param str upc: The upc number as string.
:param writer: barcode.writer instance. The writer to render the
barcode (default: SVGWriter).
:param bool make_ean: Indicates if a leading zero should be added to
the barcode. This converts the UPC into a valid European Article
Number (EAN).
"""
self.ean = make_ean
upc = upc[: self.digits]
if not upc.isdigit():
raise IllegalCharacterError("UPC code can only contain numbers.")
if len(upc) != self.digits:
raise NumberOfDigitsError(
f"UPC must have {self.digits} digits, not {len(upc)}."
)
self.upc = upc
self.upc = f"{upc}{self.calculate_checksum()}"
self.writer = writer or self.default_writer()
def __str__(self) -> str:
if self.ean:
return "0" + self.upc
return self.upc
def get_fullcode(self):
if self.ean:
return "0" + self.upc
return self.upc
def calculate_checksum(self):
"""Calculates the checksum for UPCA/UPC codes
:return: The checksum for 'self.upc'
:rtype: int
"""
def sum_(x, y):
return int(x) + int(y)
upc = self.upc[0 : self.digits]
oddsum = reduce(sum_, upc[::2])
evensum = reduce(sum_, upc[1::2])
check = (evensum + oddsum * 3) % 10
if check == 0:
return 0
return 10 - check
def build(self):
"""Builds the barcode pattern from 'self.upc'
:return: The pattern as string
:rtype: str
"""
code = _upc.EDGE[:]
for _i, number in enumerate(self.upc[0:6]):
code += _upc.CODES["L"][int(number)]
code += _upc.MIDDLE
for number in self.upc[6:]:
code += _upc.CODES["R"][int(number)]
code += _upc.EDGE
return [code]
def to_ascii(self):
"""Returns an ascii representation of the barcode.
:rtype: str
"""
code = self.build()
for i, line in enumerate(code):
code[i] = line.replace("1", "|").replace("0", "_")
return "\n".join(code)
def render(self, writer_options=None, text=None):
options = {"module_width": 0.33}
options.update(writer_options or {})
return super().render(options, text)
UPCA = UniversalProductCodeA

View File

@@ -0,0 +1,4 @@
# file generated by setuptools_scm
# don't change, don't track in version control
__version__ = version = '0.15.1'
__version_tuple__ = version_tuple = (0, 15, 1)

View File

@@ -0,0 +1,464 @@
import gzip
import os
import xml.dom
from typing import BinaryIO
from barcode.version import version
try:
import Image
import ImageDraw
import ImageFont
except ImportError:
try:
from PIL import Image # lint:ok
from PIL import ImageDraw
from PIL import ImageFont
except ImportError:
import logging
log = logging.getLogger("pyBarcode")
log.info("Pillow not found. Image output disabled")
Image = ImageDraw = ImageFont = None # lint:ok
def mm2px(mm, dpi=300):
return (mm * dpi) / 25.4
def pt2mm(pt):
return pt * 0.352777778
def _set_attributes(element, **attributes):
for key, value in attributes.items():
element.setAttribute(key, value)
def create_svg_object(with_doctype=False):
imp = xml.dom.getDOMImplementation()
doctype = imp.createDocumentType(
"svg",
"-//W3C//DTD SVG 1.1//EN",
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd",
)
document = imp.createDocument(None, "svg", doctype if with_doctype else None)
_set_attributes(
document.documentElement, version="1.1", xmlns="http://www.w3.org/2000/svg"
)
return document
SIZE = "{0:.3f}mm"
COMMENT = f"Autogenerated with python-barcode {version}"
PATH = os.path.dirname(os.path.abspath(__file__))
class BaseWriter:
"""Baseclass for all writers.
Initializes the basic writer options. Childclasses can add more
attributes and can set them directly or using
`self.set_options(option=value)`.
:parameters:
initialize : Function
Callback for initializing the inheriting writer.
Is called: `callback_initialize(raw_code)`
paint_module : Function
Callback for painting one barcode module.
Is called: `callback_paint_module(xpos, ypos, width, color)`
paint_text : Function
Callback for painting the text under the barcode.
Is called: `callback_paint_text(xpos, ypos)` using `self.text`
as text.
finish : Function
Callback for doing something with the completely rendered
output.
Is called: `return callback_finish()` and must return the
rendered output.
"""
def __init__(
self,
initialize=None,
paint_module=None,
paint_text=None,
finish=None,
) -> None:
self._callbacks = {
"initialize": initialize,
"paint_module": paint_module,
"paint_text": paint_text,
"finish": finish,
}
self.module_width = 10
self.module_height = 10
self.font_path = os.path.join(PATH, "fonts", "DejaVuSansMono.ttf")
self.font_size = 10
self.quiet_zone = 6.5
self.background = "white"
self.foreground = "black"
self.text = ""
self.human = "" # human readable text
self.text_distance = 5
self.text_line_distance = 1
self.center_text = True
self.guard_height_factor = 1.1
self.margin_top = 1
self.margin_bottom = 1
def calculate_size(self, modules_per_line, number_of_lines):
"""Calculates the size of the barcode in pixel.
:parameters:
modules_per_line : Integer
Number of modules in one line.
number_of_lines : Integer
Number of lines of the barcode.
:returns: Width and height of the barcode in pixel.
:rtype: Tuple
"""
width = 2 * self.quiet_zone + modules_per_line * self.module_width
height = (
self.margin_bottom + self.margin_top + self.module_height * number_of_lines
)
number_of_text_lines = len(self.text.splitlines())
if self.font_size and self.text:
height += (
pt2mm(self.font_size) / 2 * number_of_text_lines + self.text_distance
)
height += self.text_line_distance * (number_of_text_lines - 1)
return width, height
def save(self, filename, output):
"""Saves the rendered output to `filename`.
:parameters:
filename : String
Filename without extension.
output : String
The rendered output.
:returns: The full filename with extension.
:rtype: String
"""
raise NotImplementedError
def register_callback(self, action, callback):
"""Register one of the three callbacks if not given at instance
creation.
:parameters:
action : String
One of 'initialize', 'paint_module', 'paint_text', 'finish'.
callback : Function
The callback function for the given action.
"""
self._callbacks[action] = callback
def set_options(self, options):
"""Sets the given options as instance attributes (only
if they are known).
:parameters:
options : Dict
All known instance attributes and more if the childclass
has defined them before this call.
:rtype: None
"""
for key, val in options.items():
key = key.lstrip("_")
if hasattr(self, key):
setattr(self, key, val)
def packed(self, line):
"""
Pack line to list give better gfx result, otherwise in can
result in aliasing gaps
'11010111' -> [2, -1, 1, -1, 3]
This method will yield a sequence of pairs (width, height_factor).
:parameters:
line: String
A string matching the writer spec
(only contain 0 or 1 or G).
"""
line += " "
c = 1
for i in range(0, len(line) - 1):
if line[i] == line[i + 1]:
c += 1
else:
if line[i] == "1":
yield (c, 1)
elif line[i] == "G":
yield (c, self.guard_height_factor)
else:
yield (-c, self.guard_height_factor)
c = 1
def render(self, code):
"""Renders the barcode to whatever the inheriting writer provides,
using the registered callbacks.
:parameters:
code : List
List of strings matching the writer spec
(only contain 0 or 1 or G).
"""
if self._callbacks["initialize"] is not None:
self._callbacks["initialize"](code)
ypos = self.margin_top
base_height = self.module_height
for cc, line in enumerate(code):
# Left quiet zone is x startposition
xpos = self.quiet_zone
bxs = xpos # x start of barcode
text = {
"start": [], # The x start of a guard
"end": [], # The x end of a guard
"xpos": [], # The x position where to write a text block
# Flag that indicates if the previous mod was part of an guard block:
"was_guard": False,
}
for mod, height_factor in self.packed(line):
if mod < 1:
color = self.background
else:
color = self.foreground
if text["was_guard"] and height_factor == 1:
# The current guard ended, store its x position
text["end"].append(xpos)
text["was_guard"] = False
elif not text["was_guard"] and height_factor != 1:
# A guard started, store its x position
text["start"].append(xpos)
text["was_guard"] = True
self.module_height = base_height * height_factor
# remove painting for background colored tiles?
self._callbacks["paint_module"](
xpos, ypos, self.module_width * abs(mod), color
)
xpos += self.module_width * abs(mod)
else:
if height_factor != 1:
text["end"].append(xpos)
self.module_height = base_height
bxe = xpos
# Add right quiet zone to every line, except last line,
# quiet zone already provided with background,
# should it be removed completely?
if (cc + 1) != len(code):
self._callbacks["paint_module"](
xpos, ypos, self.quiet_zone, self.background
)
ypos += self.module_height
if self.text and self._callbacks["paint_text"] is not None:
if not text["start"]:
# If we don't have any start value, print the entire ean
ypos += self.text_distance
xpos = bxs + (bxe - bxs) / 2.0 if self.center_text else bxs
self._callbacks["paint_text"](xpos, ypos)
else:
# Else, divide the ean into blocks and print each block
# in the expected position.
text["xpos"] = [bxs - 4 * self.module_width]
# Calculates the position of the text by getting the difference
# between a guard end and the next start
text["start"].pop(0)
for s, e in zip(text["start"], text["end"]):
text["xpos"].append(e + (s - e) / 2)
# The last text block is always put after the last guard end
text["xpos"].append(text["end"][-1] + 4 * self.module_width)
# Split the ean into its blocks
self.text = self.text.split(" ")
ypos += pt2mm(self.font_size)
blocks = self.text
for text_, xpos in zip(blocks, text["xpos"]):
self.text = text_
self._callbacks["paint_text"](xpos, ypos)
return self._callbacks["finish"]()
class SVGWriter(BaseWriter):
def __init__(self) -> None:
BaseWriter.__init__(
self, self._init, self._create_module, self._create_text, self._finish
)
self.compress = False
self.with_doctype = True
self._document = None
self._root = None
self._group = None
def _init(self, code):
width, height = self.calculate_size(len(code[0]), len(code))
self._document = create_svg_object(self.with_doctype)
self._root = self._document.documentElement
attributes = {
"width": SIZE.format(width),
"height": SIZE.format(height),
}
_set_attributes(self._root, **attributes)
if COMMENT:
self._root.appendChild(self._document.createComment(COMMENT))
# create group for easier handling in 3rd party software
# like corel draw, inkscape, ...
group = self._document.createElement("g")
attributes = {"id": "barcode_group"}
_set_attributes(group, **attributes)
self._group = self._root.appendChild(group)
background = self._document.createElement("rect")
attributes = {
"width": "100%",
"height": "100%",
"style": f"fill:{self.background}",
}
_set_attributes(background, **attributes)
self._group.appendChild(background)
def _create_module(self, xpos, ypos, width, color):
# Background rect has been provided already, so skipping "spaces"
if color != self.background:
element = self._document.createElement("rect")
attributes = {
"x": SIZE.format(xpos),
"y": SIZE.format(ypos),
"width": SIZE.format(width),
"height": SIZE.format(self.module_height),
"style": f"fill:{color};",
}
_set_attributes(element, **attributes)
self._group.appendChild(element)
def _create_text(self, xpos, ypos):
# check option to override self.text with self.human (barcode as
# human readable data, can be used to print own formats)
barcodetext = self.human if self.human != "" else self.text
for subtext in barcodetext.split("\n"):
element = self._document.createElement("text")
attributes = {
"x": SIZE.format(xpos),
"y": SIZE.format(ypos),
"style": "fill:{};font-size:{}pt;text-anchor:middle;".format(
self.foreground,
self.font_size,
),
}
_set_attributes(element, **attributes)
text_element = self._document.createTextNode(subtext)
element.appendChild(text_element)
self._group.appendChild(element)
ypos += pt2mm(self.font_size) + self.text_line_distance
def _finish(self):
if self.compress:
return self._document.toxml(encoding="UTF-8")
return self._document.toprettyxml(
indent=4 * " ", newl=os.linesep, encoding="UTF-8"
)
def save(self, filename, output):
if self.compress:
_filename = f"{filename}.svgz"
f = gzip.open(_filename, "wb")
f.write(output)
f.close()
else:
_filename = f"{filename}.svg"
with open(_filename, "wb") as f:
f.write(output)
return _filename
def write(self, content, fp: BinaryIO):
"""Write `content` into a file-like object.
Content should be a barcode rendered by this writer.
"""
fp.write(content)
if Image is None:
ImageWriter = None
else:
class ImageWriter(BaseWriter): # type: ignore[no-redef]
format: str
mode: str
dpi: int
def __init__(self, format="PNG", mode="RGB") -> None:
"""Initialise a new write instance.
:params format: The file format for the generated image. This parameter can
take any value that Pillow accepts.
:params mode: The colour-mode for the generated image. Set this to RGBA if
you wish to use colours with transparency.
"""
BaseWriter.__init__(
self, self._init, self._paint_module, self._paint_text, self._finish
)
self.format = format
self.mode = mode
self.dpi = 300
self._image = None
self._draw = None
def _init(self, code):
width, height = self.calculate_size(len(code[0]), len(code))
size = (int(mm2px(width, self.dpi)), int(mm2px(height, self.dpi)))
self._image = Image.new(self.mode, size, self.background)
self._draw = ImageDraw.Draw(self._image)
def _paint_module(self, xpos, ypos, width, color):
size = [
(mm2px(xpos, self.dpi), mm2px(ypos, self.dpi)),
(
mm2px(xpos + width, self.dpi) - 1,
mm2px(ypos + self.module_height, self.dpi),
),
]
self._draw.rectangle(size, outline=color, fill=color)
def _paint_text(self, xpos, ypos):
font_size = int(mm2px(pt2mm(self.font_size), self.dpi))
font = ImageFont.truetype(self.font_path, font_size)
for subtext in self.text.split("\n"):
pos = (
mm2px(xpos, self.dpi),
mm2px(ypos, self.dpi),
)
self._draw.text(
pos, subtext, font=font, fill=self.foreground, anchor="md"
)
ypos += pt2mm(self.font_size) / 2 + self.text_line_distance
def _finish(self):
return self._image
def save(self, filename, output):
filename = f"{filename}.{self.format.lower()}"
output.save(filename, self.format.upper())
return filename
def write(self, content, fp: BinaryIO):
"""Write `content` into a file-like object.
Content should be a barcode rendered by this writer.
"""
content.save(fp, format=self.format)