-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated Django QR Code to use Segno instead of qrcode / Pillow. #13
Removes the requirement "Pillow" since Segno creates PNG images without further dependencies Support for other colors than black and white, each module type may have its own color. Added support for Micro QR codes. Removed the image.py module, not needed anymore.
- Loading branch information
1 parent
8ba89c2
commit c2ebbc8
Showing
64 changed files
with
916 additions
and
852 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 4 | ||
insert_final_newline = true | ||
charset = utf-8 | ||
|
||
[*.html] | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,116 +1,75 @@ | ||
"""Tools for generating QR codes. This module depends on the qrcode python library.""" | ||
|
||
import base64 | ||
from io import BytesIO | ||
|
||
import xml.etree.ElementTree as ET | ||
|
||
"""Tools for generating QR codes. This module depends on the Segno library.""" | ||
import io | ||
from django.utils.encoding import force_str | ||
from django.utils.html import escape | ||
from django.utils.safestring import mark_safe | ||
|
||
from qr_code.qrcode.constants import SIZE_DICT, ERROR_CORRECTION_DICT, DEFAULT_ERROR_CORRECTION, DEFAULT_MODULE_SIZE, \ | ||
DEFAULT_CACHE_ENABLED, DEFAULT_URL_SIGNATURE_ENABLED | ||
from qr_code.qrcode.image import SvgPathImage, PilImageOrFallback, SVG_FORMAT_NAME, PNG_FORMAT_NAME | ||
import segno | ||
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 | ||
|
||
|
||
class SvgEmbeddedInHtmlImage(SvgPathImage): | ||
def _write(self, stream): | ||
self._img.append(self.make_path()) | ||
ET.ElementTree(self._img).write(stream, encoding="UTF-8", xml_declaration=False, default_namespace=None, | ||
method='html') | ||
|
||
|
||
def make_qr_code_image(text, image_factory, qr_code_options=QRCodeOptions()): | ||
""" | ||
Generates an image object (from the qrcode library) representing the QR code for the given text. | ||
def make_qr(text, qr_code_options): | ||
"""Creates a QR code | ||
Any invalid argument is silently converted into the default value for that argument. | ||
:rtype: segno.QRCode | ||
""" | ||
|
||
valid_version = _get_valid_version_or_none(qr_code_options.version) | ||
valid_size = _get_valid_size_or_default(qr_code_options.size) | ||
valid_error_correction = _get_valid_error_correction_or_default(qr_code_options.error_correction) | ||
import qrcode | ||
qr = qrcode.QRCode( | ||
version=valid_version, | ||
error_correction=valid_error_correction, | ||
box_size=valid_size, | ||
border=qr_code_options.border | ||
) | ||
qr.add_data(force_str(text)) | ||
if valid_version is None: | ||
qr.make(fit=True) | ||
return qr.make_image(image_factory=image_factory) | ||
|
||
|
||
def _get_valid_error_correction_or_default(error_correction): | ||
return ERROR_CORRECTION_DICT.get(error_correction.upper(), ERROR_CORRECTION_DICT[ | ||
DEFAULT_ERROR_CORRECTION]) | ||
|
||
|
||
def _get_valid_size_or_default(size): | ||
if _can_be_cast_to_int(size): | ||
actual_size = int(size) | ||
if actual_size < 1: | ||
actual_size = SIZE_DICT[DEFAULT_MODULE_SIZE.lower()] | ||
elif isinstance(size, str): | ||
actual_size = SIZE_DICT.get(size.lower(), DEFAULT_MODULE_SIZE) | ||
else: | ||
actual_size = SIZE_DICT[DEFAULT_MODULE_SIZE.lower()] | ||
return actual_size | ||
return segno.make(force_str(text), **qr_code_options.kw_make()) | ||
|
||
|
||
def _get_valid_version_or_none(version): | ||
if _can_be_cast_to_int(version): | ||
actual_version = int(version) | ||
if actual_version < 1 or actual_version > 40: | ||
actual_version = None | ||
else: | ||
actual_version = None | ||
return actual_version | ||
|
||
def make_qr_code_image(text, qr_code_options): | ||
""" | ||
Returns a bytes object representing a QR code image for the provided text. | ||
def _can_be_cast_to_int(value): | ||
return isinstance(value, int) or (isinstance(value, str) and value.isdigit()) | ||
:param str text: The text to encode | ||
:param qr_code_options: Options to create and serialize the QR code. | ||
:rtype: bytes | ||
""" | ||
qr = make_qr(text, qr_code_options) | ||
out = io.BytesIO() | ||
qr.save(out, **qr_code_options.kw_save()) | ||
return out.getvalue() | ||
|
||
|
||
def make_embedded_qr_code(text, qr_code_options=QRCodeOptions()): | ||
def make_embedded_qr_code(text, qr_code_options): | ||
""" | ||
Generates a <svg> or <img> tag representing the QR code for the given text. This tag can be embedded into an | ||
HTML document. | ||
Generates a <svg> or <img> tag representing the QR code for the given text. | ||
This tag can be embedded into an HTML document. | ||
""" | ||
image_format = qr_code_options.image_format | ||
img = make_qr_code_image(text, SvgEmbeddedInHtmlImage if image_format == SVG_FORMAT_NAME else PilImageOrFallback, qr_code_options=qr_code_options) | ||
stream = BytesIO() | ||
if image_format == SVG_FORMAT_NAME: | ||
img.save(stream, kind=SVG_FORMAT_NAME.upper()) | ||
html_fragment = (str(stream.getvalue(), 'utf-8')) | ||
else: | ||
img.save(stream, format=PNG_FORMAT_NAME.upper()) | ||
html_fragment = '<img src="data:image/png;base64, %s" alt="%s">' % (str(base64.b64encode(stream.getvalue()), encoding='ascii'), escape(text)) | ||
return mark_safe(html_fragment) | ||
qr = make_qr(text, qr_code_options) | ||
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': | ||
return mark_safe('<img src="{0}" alt="{1}">' | ||
.format(qr.png_data_uri(**kw), escape(text))) | ||
return mark_safe(qr.svg_inline(**kw)) | ||
|
||
|
||
def make_qr_code_with_args(text, qr_code_args): | ||
options = qr_code_args.get('options') | ||
if options: | ||
if not isinstance(options, QRCodeOptions): | ||
raise TypeError('The options argument must be of type QRCodeOptions.') | ||
else: | ||
options = QRCodeOptions(**qr_code_args) | ||
options = _options_from_args(qr_code_args) | ||
return make_embedded_qr_code(text, options) | ||
|
||
|
||
def make_qr_code_url_with_args(text, qr_code_args): | ||
cache_enabled = qr_code_args.pop('cache_enabled', DEFAULT_CACHE_ENABLED) | ||
url_signature_enabled = qr_code_args.pop('url_signature_enabled', DEFAULT_URL_SIGNATURE_ENABLED) | ||
options = qr_code_args.get('options') | ||
options = _options_from_args(qr_code_args) | ||
return make_qr_code_url(text, options, cache_enabled=cache_enabled, | ||
url_signature_enabled=url_signature_enabled) | ||
|
||
|
||
def _options_from_args(args): | ||
"""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: | ||
options = QRCodeOptions(**qr_code_args) | ||
return make_qr_code_url(text, options, cache_enabled=cache_enabled, url_signature_enabled=url_signature_enabled) | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.