Source code for qr_code.qrcode.maker
"""Tools for generating QR codes. This module depends on the Segno library."""
import io
from typing import Mapping, Any
from django.utils.html import escape
from django.utils.safestring import mark_safe
import segno
from pydantic import validate_arguments
from qr_code.qrcode import PYDANTIC_CONFIG
from qr_code.qrcode.constants import DEFAULT_CACHE_ENABLED, DEFAULT_URL_SIGNATURE_ENABLED
from qr_code.qrcode.serve import make_qr_code_url
from qr_code.qrcode.utils import QRCodeOptions
[docs]@validate_arguments(config=PYDANTIC_CONFIG)
def make_qr(data: Any, qr_code_options: QRCodeOptions, force_text: bool = True):
"""Creates a QR code that encodes the given `data` with the given `qr_code_options`.
:param str data: The data to encode
:param qr_code_options: Options to create and serialize the QR code.
:param bool force_text: Tells whether we want to force the `data` to be considered as text string and encoded in byte mode.
:rtype: segno.QRCode
"""
# WARNING: For compatibility reasons, we still allow to pass __proxy__ class (lazy string). Moreover, it would be OK to pass anything that has __str__
# attribute (e. g. class instance that handles phone numbers).
if force_text:
return segno.make(str(data), **qr_code_options.kw_make(), mode="byte")
return segno.make(data, **qr_code_options.kw_make())
[docs]@validate_arguments(config=PYDANTIC_CONFIG)
def make_qr_code_image(data: Any, qr_code_options: QRCodeOptions, force_text: bool = True) -> bytes:
"""
Creates a bytes object representing a QR code image for the provided `data`.
:param str data: The data to encode
:param qr_code_options: Options to create and serialize the QR code.
:param bool force_text: Tells whether we want to force the `data` to be considered as text string and encoded in byte mode.
:rtype: bytes
"""
qr = make_qr(data, qr_code_options, force_text=force_text)
out = io.BytesIO()
qr.save(out, **qr_code_options.kw_save())
return out.getvalue()
[docs]@validate_arguments(config=PYDANTIC_CONFIG)
def make_embedded_qr_code(data: Any, qr_code_options: QRCodeOptions, force_text: bool = True) -> str:
"""
Generates a <svg> or <img> tag representing the QR code for the given `data`.
This tag can be embedded into an HTML document.
"""
qr = make_qr(data, qr_code_options, force_text=force_text)
kw = qr_code_options.kw_save()
# Pop the image format from the keywords since qr.png_data_uri / qr.svg_inline
# set it automatically
kw.pop("kind")
if qr_code_options.image_format == "png":
if isinstance(data, bytes):
alt = ""
encodings = ["utf-8", "iso-8859-1", "shift-jis"]
if qr_code_options.encoding:
ei = encodings.index(qr_code_options.encoding)
if ei > 0:
encodings[ei] = encodings[0]
encodings[0] = qr_code_options.encoding
for e in encodings:
try:
alt = data.decode(e)
break
except UnicodeDecodeError:
pass
elif not isinstance(data, str):
alt = str(data)
else:
alt = data
return mark_safe(f'<img src="{qr.png_data_uri(**kw)}" alt="{escape(alt)}">')
return mark_safe(qr.svg_inline(**kw))
def make_qr_code_with_args(data: Any, qr_code_args: dict, force_text: bool = True) -> str:
options = _options_from_args(qr_code_args)
return make_embedded_qr_code(data, options, force_text=force_text)
def make_qr_code_url_with_args(data: Any, qr_code_args: dict, force_text: bool = True) -> str:
cache_enabled = qr_code_args.pop("cache_enabled", DEFAULT_CACHE_ENABLED)
if not isinstance(cache_enabled, bool):
cache_enabled = not cache_enabled == "False"
url_signature_enabled = qr_code_args.pop("url_signature_enabled", DEFAULT_URL_SIGNATURE_ENABLED)
if not isinstance(url_signature_enabled, bool):
url_signature_enabled = not url_signature_enabled == "False"
options = _options_from_args(qr_code_args)
return make_qr_code_url(data, options, force_text=force_text, cache_enabled=cache_enabled, url_signature_enabled=url_signature_enabled)
def _options_from_args(args: Mapping) -> QRCodeOptions:
"""Returns a QRCodeOptions instance from the provided arguments."""
options = args.get("options")
if options:
if not isinstance(options, QRCodeOptions):
raise TypeError("The options argument must be of type QRCodeOptions.")
else:
# Convert the string "None" into None
kw = {k: v if v != "None" else None for k, v in args.items()}
options = QRCodeOptions(**kw)
return options