updates
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
164
Backend/venv/lib/python3.12/site-packages/qrcode/image/base.py
Normal file
164
Backend/venv/lib/python3.12/site-packages/qrcode/image/base.py
Normal file
@@ -0,0 +1,164 @@
|
||||
import abc
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Type, Union
|
||||
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qrcode.main import ActiveWithNeighbors, QRCode
|
||||
|
||||
|
||||
DrawerAliases = Dict[str, Tuple[Type[QRModuleDrawer], Dict[str, Any]]]
|
||||
|
||||
|
||||
class BaseImage:
|
||||
"""
|
||||
Base QRCode image output class.
|
||||
"""
|
||||
|
||||
kind: Optional[str] = None
|
||||
allowed_kinds: Optional[Tuple[str]] = None
|
||||
needs_context = False
|
||||
needs_processing = False
|
||||
needs_drawrect = True
|
||||
|
||||
def __init__(self, border, width, box_size, *args, **kwargs):
|
||||
self.border = border
|
||||
self.width = width
|
||||
self.box_size = box_size
|
||||
self.pixel_size = (self.width + self.border * 2) * self.box_size
|
||||
self.modules = kwargs.pop("qrcode_modules")
|
||||
self._img = self.new_image(**kwargs)
|
||||
self.init_new_image()
|
||||
|
||||
@abc.abstractmethod
|
||||
def drawrect(self, row, col):
|
||||
"""
|
||||
Draw a single rectangle of the QR code.
|
||||
"""
|
||||
|
||||
def drawrect_context(self, row: int, col: int, qr: "QRCode"):
|
||||
"""
|
||||
Draw a single rectangle of the QR code given the surrounding context
|
||||
"""
|
||||
raise NotImplementedError("BaseImage.drawrect_context") # pragma: no cover
|
||||
|
||||
def process(self):
|
||||
"""
|
||||
Processes QR code after completion
|
||||
"""
|
||||
raise NotImplementedError("BaseImage.drawimage") # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def save(self, stream, kind=None):
|
||||
"""
|
||||
Save the image file.
|
||||
"""
|
||||
|
||||
def pixel_box(self, row, col):
|
||||
"""
|
||||
A helper method for pixel-based image generators that specifies the
|
||||
four pixel coordinates for a single rect.
|
||||
"""
|
||||
x = (col + self.border) * self.box_size
|
||||
y = (row + self.border) * self.box_size
|
||||
return (
|
||||
(x, y),
|
||||
(x + self.box_size - 1, y + self.box_size - 1),
|
||||
)
|
||||
|
||||
@abc.abstractmethod
|
||||
def new_image(self, **kwargs) -> Any:
|
||||
"""
|
||||
Build the image class. Subclasses should return the class created.
|
||||
"""
|
||||
|
||||
def init_new_image(self):
|
||||
pass
|
||||
|
||||
def get_image(self, **kwargs):
|
||||
"""
|
||||
Return the image class for further processing.
|
||||
"""
|
||||
return self._img
|
||||
|
||||
def check_kind(self, kind, transform=None):
|
||||
"""
|
||||
Get the image type.
|
||||
"""
|
||||
if kind is None:
|
||||
kind = self.kind
|
||||
allowed = not self.allowed_kinds or kind in self.allowed_kinds
|
||||
if transform:
|
||||
kind = transform(kind)
|
||||
if not allowed:
|
||||
allowed = kind in self.allowed_kinds
|
||||
if not allowed:
|
||||
raise ValueError(f"Cannot set {type(self).__name__} type to {kind}")
|
||||
return kind
|
||||
|
||||
def is_eye(self, row: int, col: int):
|
||||
"""
|
||||
Find whether the referenced module is in an eye.
|
||||
"""
|
||||
return (
|
||||
(row < 7 and col < 7)
|
||||
or (row < 7 and self.width - col < 8)
|
||||
or (self.width - row < 8 and col < 7)
|
||||
)
|
||||
|
||||
|
||||
class BaseImageWithDrawer(BaseImage):
|
||||
default_drawer_class: Type[QRModuleDrawer]
|
||||
drawer_aliases: DrawerAliases = {}
|
||||
|
||||
def get_default_module_drawer(self) -> QRModuleDrawer:
|
||||
return self.default_drawer_class()
|
||||
|
||||
def get_default_eye_drawer(self) -> QRModuleDrawer:
|
||||
return self.default_drawer_class()
|
||||
|
||||
needs_context = True
|
||||
|
||||
module_drawer: "QRModuleDrawer"
|
||||
eye_drawer: "QRModuleDrawer"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
module_drawer: Union[QRModuleDrawer, str, None] = None,
|
||||
eye_drawer: Union[QRModuleDrawer, str, None] = None,
|
||||
**kwargs,
|
||||
):
|
||||
self.module_drawer = (
|
||||
self.get_drawer(module_drawer) or self.get_default_module_drawer()
|
||||
)
|
||||
# The eye drawer can be overridden by another module drawer as well,
|
||||
# but you have to be more careful with these in order to make the QR
|
||||
# code still parseable
|
||||
self.eye_drawer = self.get_drawer(eye_drawer) or self.get_default_eye_drawer()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_drawer(
|
||||
self, drawer: Union[QRModuleDrawer, str, None]
|
||||
) -> Optional[QRModuleDrawer]:
|
||||
if not isinstance(drawer, str):
|
||||
return drawer
|
||||
drawer_cls, kwargs = self.drawer_aliases[drawer]
|
||||
return drawer_cls(**kwargs)
|
||||
|
||||
def init_new_image(self):
|
||||
self.module_drawer.initialize(img=self)
|
||||
self.eye_drawer.initialize(img=self)
|
||||
|
||||
return super().init_new_image()
|
||||
|
||||
def drawrect_context(self, row: int, col: int, qr: "QRCode"):
|
||||
box = self.pixel_box(row, col)
|
||||
drawer = self.eye_drawer if self.is_eye(row, col) else self.module_drawer
|
||||
is_active: Union[bool, ActiveWithNeighbors] = (
|
||||
qr.active_with_neighbors(row, col)
|
||||
if drawer.needs_neighbors
|
||||
else bool(qr.modules[row][col])
|
||||
)
|
||||
|
||||
drawer.drawrect(box, is_active)
|
||||
@@ -0,0 +1,54 @@
|
||||
import qrcode.image.base
|
||||
from qrcode.compat.pil import Image, ImageDraw
|
||||
|
||||
|
||||
class PilImage(qrcode.image.base.BaseImage):
|
||||
"""
|
||||
PIL image builder, default format is PNG.
|
||||
"""
|
||||
|
||||
kind = "PNG"
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
back_color = kwargs.get("back_color", "white")
|
||||
fill_color = kwargs.get("fill_color", "black")
|
||||
|
||||
try:
|
||||
fill_color = fill_color.lower()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
back_color = back_color.lower()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# L mode (1 mode) color = (r*299 + g*587 + b*114)//1000
|
||||
if fill_color == "black" and back_color == "white":
|
||||
mode = "1"
|
||||
fill_color = 0
|
||||
if back_color == "white":
|
||||
back_color = 255
|
||||
elif back_color == "transparent":
|
||||
mode = "RGBA"
|
||||
back_color = None
|
||||
else:
|
||||
mode = "RGB"
|
||||
|
||||
img = Image.new(mode, (self.pixel_size, self.pixel_size), back_color)
|
||||
self.fill_color = fill_color
|
||||
self._idr = ImageDraw.Draw(img)
|
||||
return img
|
||||
|
||||
def drawrect(self, row, col):
|
||||
box = self.pixel_box(row, col)
|
||||
self._idr.rectangle(box, fill=self.fill_color)
|
||||
|
||||
def save(self, stream, format=None, **kwargs):
|
||||
kind = kwargs.pop("kind", self.kind)
|
||||
if format is None:
|
||||
format = kind
|
||||
self._img.save(stream, format=format, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._img, name)
|
||||
@@ -0,0 +1,54 @@
|
||||
from itertools import chain
|
||||
|
||||
import png
|
||||
|
||||
import qrcode.image.base
|
||||
|
||||
|
||||
class PyPNGImage(qrcode.image.base.BaseImage):
|
||||
"""
|
||||
pyPNG image builder.
|
||||
"""
|
||||
|
||||
kind = "PNG"
|
||||
allowed_kinds = ("PNG",)
|
||||
needs_drawrect = False
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
return png.Writer(self.pixel_size, self.pixel_size, greyscale=True, bitdepth=1)
|
||||
|
||||
def drawrect(self, row, col):
|
||||
"""
|
||||
Not used.
|
||||
"""
|
||||
|
||||
def save(self, stream, kind=None):
|
||||
if isinstance(stream, str):
|
||||
stream = open(stream, "wb")
|
||||
self._img.write(stream, self.rows_iter())
|
||||
|
||||
def rows_iter(self):
|
||||
yield from self.border_rows_iter()
|
||||
border_col = [1] * (self.box_size * self.border)
|
||||
for module_row in self.modules:
|
||||
row = (
|
||||
border_col
|
||||
+ list(
|
||||
chain.from_iterable(
|
||||
([not point] * self.box_size) for point in module_row
|
||||
)
|
||||
)
|
||||
+ border_col
|
||||
)
|
||||
for _ in range(self.box_size):
|
||||
yield row
|
||||
yield from self.border_rows_iter()
|
||||
|
||||
def border_rows_iter(self):
|
||||
border_row = [1] * (self.box_size * (self.width + self.border * 2))
|
||||
for _ in range(self.border * self.box_size):
|
||||
yield border_row
|
||||
|
||||
|
||||
# Keeping this for backwards compatibility.
|
||||
PymagingImage = PyPNGImage
|
||||
@@ -0,0 +1,112 @@
|
||||
# Needed on case-insensitive filesystems
|
||||
from __future__ import absolute_import
|
||||
|
||||
import qrcode.image.base
|
||||
from qrcode.compat.pil import Image
|
||||
from qrcode.image.styles.colormasks import QRColorMask, SolidFillColorMask
|
||||
from qrcode.image.styles.moduledrawers import SquareModuleDrawer
|
||||
|
||||
|
||||
class StyledPilImage(qrcode.image.base.BaseImageWithDrawer):
|
||||
"""
|
||||
Styled PIL image builder, default format is PNG.
|
||||
|
||||
This differs from the PilImage in that there is a module_drawer, a
|
||||
color_mask, and an optional image
|
||||
|
||||
The module_drawer should extend the QRModuleDrawer class and implement the
|
||||
drawrect_context(self, box, active, context), and probably also the
|
||||
initialize function. This will draw an individual "module" or square on
|
||||
the QR code.
|
||||
|
||||
The color_mask will extend the QRColorMask class and will at very least
|
||||
implement the get_fg_pixel(image, x, y) function, calculating a color to
|
||||
put on the image at the pixel location (x,y) (more advanced functionality
|
||||
can be gotten by instead overriding other functions defined in the
|
||||
QRColorMask class)
|
||||
|
||||
The Image can be specified either by path or with a Pillow Image, and if it
|
||||
is there will be placed in the middle of the QR code. No effort is done to
|
||||
ensure that the QR code is still legible after the image has been placed
|
||||
there; Q or H level error correction levels are recommended to maintain
|
||||
data integrity A resampling filter can be specified (defaulting to
|
||||
PIL.Image.Resampling.LANCZOS) for resizing; see PIL.Image.resize() for possible
|
||||
options for this parameter.
|
||||
"""
|
||||
|
||||
kind = "PNG"
|
||||
|
||||
needs_processing = True
|
||||
color_mask: QRColorMask
|
||||
default_drawer_class = SquareModuleDrawer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.color_mask = kwargs.get("color_mask", SolidFillColorMask())
|
||||
embeded_image_path = kwargs.get("embeded_image_path", None)
|
||||
self.embeded_image = kwargs.get("embeded_image", None)
|
||||
self.embeded_image_resample = kwargs.get(
|
||||
"embeded_image_resample", Image.Resampling.LANCZOS
|
||||
)
|
||||
if not self.embeded_image and embeded_image_path:
|
||||
self.embeded_image = Image.open(embeded_image_path)
|
||||
|
||||
# the paint_color is the color the module drawer will use to draw upon
|
||||
# a canvas During the color mask process, pixels that are paint_color
|
||||
# are replaced by a newly-calculated color
|
||||
self.paint_color = tuple(0 for i in self.color_mask.back_color)
|
||||
if self.color_mask.has_transparency:
|
||||
self.paint_color = tuple([*self.color_mask.back_color[:3], 255])
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
mode = (
|
||||
"RGBA"
|
||||
if (
|
||||
self.color_mask.has_transparency
|
||||
or (self.embeded_image and "A" in self.embeded_image.getbands())
|
||||
)
|
||||
else "RGB"
|
||||
)
|
||||
# This is the background color. Should be white or whiteish
|
||||
back_color = self.color_mask.back_color
|
||||
|
||||
return Image.new(mode, (self.pixel_size, self.pixel_size), back_color)
|
||||
|
||||
def init_new_image(self):
|
||||
self.color_mask.initialize(self, self._img)
|
||||
super().init_new_image()
|
||||
|
||||
def process(self):
|
||||
self.color_mask.apply_mask(self._img)
|
||||
if self.embeded_image:
|
||||
self.draw_embeded_image()
|
||||
|
||||
def draw_embeded_image(self):
|
||||
if not self.embeded_image:
|
||||
return
|
||||
total_width, _ = self._img.size
|
||||
total_width = int(total_width)
|
||||
logo_width_ish = int(total_width / 4)
|
||||
logo_offset = (
|
||||
int((int(total_width / 2) - int(logo_width_ish / 2)) / self.box_size)
|
||||
* self.box_size
|
||||
) # round the offset to the nearest module
|
||||
logo_position = (logo_offset, logo_offset)
|
||||
logo_width = total_width - logo_offset * 2
|
||||
region = self.embeded_image
|
||||
region = region.resize((logo_width, logo_width), self.embeded_image_resample)
|
||||
if "A" in region.getbands():
|
||||
self._img.alpha_composite(region, logo_position)
|
||||
else:
|
||||
self._img.paste(region, logo_position)
|
||||
|
||||
def save(self, stream, format=None, **kwargs):
|
||||
if format is None:
|
||||
format = kwargs.get("kind", self.kind)
|
||||
if "kind" in kwargs:
|
||||
del kwargs["kind"]
|
||||
self._img.save(stream, format=format, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._img, name)
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,220 @@
|
||||
# Needed on case-insensitive filesystems
|
||||
from __future__ import absolute_import
|
||||
|
||||
import math
|
||||
|
||||
from qrcode.compat.pil import Image
|
||||
|
||||
|
||||
class QRColorMask:
|
||||
"""
|
||||
QRColorMask is used to color in the QRCode.
|
||||
|
||||
By the time apply_mask is called, the QRModuleDrawer of the StyledPilImage
|
||||
will have drawn all of the modules on the canvas (the color of these
|
||||
modules will be mostly black, although antialiasing may result in
|
||||
gradients) In the base class, apply_mask is implemented such that the
|
||||
background color will remain, but the foreground pixels will be replaced by
|
||||
a color determined by a call to get_fg_pixel. There is additional
|
||||
calculation done to preserve the gradient artifacts of antialiasing.
|
||||
|
||||
All QRColorMask objects should be careful about RGB vs RGBA color spaces.
|
||||
|
||||
For examples of what these look like, see doc/color_masks.png
|
||||
"""
|
||||
|
||||
back_color = (255, 255, 255)
|
||||
has_transparency = False
|
||||
paint_color = back_color
|
||||
|
||||
def initialize(self, styledPilImage, image):
|
||||
self.paint_color = styledPilImage.paint_color
|
||||
|
||||
def apply_mask(self, image):
|
||||
width, height = image.size
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
norm = self.extrap_color(
|
||||
self.back_color, self.paint_color, image.getpixel((x, y))
|
||||
)
|
||||
if norm is not None:
|
||||
image.putpixel(
|
||||
(x, y),
|
||||
self.interp_color(
|
||||
self.get_bg_pixel(image, x, y),
|
||||
self.get_fg_pixel(image, x, y),
|
||||
norm,
|
||||
),
|
||||
)
|
||||
else:
|
||||
image.putpixel((x, y), self.get_bg_pixel(image, x, y))
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
raise NotImplementedError("QRModuleDrawer.paint_fg_pixel")
|
||||
|
||||
def get_bg_pixel(self, image, x, y):
|
||||
return self.back_color
|
||||
|
||||
# The following functions are helpful for color calculation:
|
||||
|
||||
# interpolate a number between two numbers
|
||||
def interp_num(self, n1, n2, norm):
|
||||
return int(n2 * norm + n1 * (1 - norm))
|
||||
|
||||
# interpolate a color between two colorrs
|
||||
def interp_color(self, col1, col2, norm):
|
||||
return tuple(self.interp_num(col1[i], col2[i], norm) for i in range(len(col1)))
|
||||
|
||||
# find the interpolation coefficient between two numbers
|
||||
def extrap_num(self, n1, n2, interped_num):
|
||||
if n2 == n1:
|
||||
return None
|
||||
else:
|
||||
return (interped_num - n1) / (n2 - n1)
|
||||
|
||||
# find the interpolation coefficient between two numbers
|
||||
def extrap_color(self, col1, col2, interped_color):
|
||||
normed = []
|
||||
for c1, c2, ci in zip(col1, col2, interped_color):
|
||||
extrap = self.extrap_num(c1, c2, ci)
|
||||
if extrap is not None:
|
||||
normed.append(extrap)
|
||||
if not normed:
|
||||
return None
|
||||
return sum(normed) / len(normed)
|
||||
|
||||
|
||||
class SolidFillColorMask(QRColorMask):
|
||||
"""
|
||||
Just fills in the background with one color and the foreground with another
|
||||
"""
|
||||
|
||||
def __init__(self, back_color=(255, 255, 255), front_color=(0, 0, 0)):
|
||||
self.back_color = back_color
|
||||
self.front_color = front_color
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def apply_mask(self, image):
|
||||
if self.back_color == (255, 255, 255) and self.front_color == (0, 0, 0):
|
||||
# Optimization: the image is already drawn by QRModuleDrawer in
|
||||
# black and white, so if these are also our mask colors we don't
|
||||
# need to do anything. This is much faster than actually applying a
|
||||
# mask.
|
||||
pass
|
||||
else:
|
||||
# TODO there's probably a way to use PIL.ImageMath instead of doing
|
||||
# the individual pixel comparisons that the base class uses, which
|
||||
# would be a lot faster. (In fact doing this would probably remove
|
||||
# the need for the B&W optimization above.)
|
||||
QRColorMask.apply_mask(self, image)
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
return self.front_color
|
||||
|
||||
|
||||
class RadialGradiantColorMask(QRColorMask):
|
||||
"""
|
||||
Fills in the foreground with a radial gradient from the center to the edge
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, back_color=(255, 255, 255), center_color=(0, 0, 0), edge_color=(0, 0, 255)
|
||||
):
|
||||
self.back_color = back_color
|
||||
self.center_color = center_color
|
||||
self.edge_color = edge_color
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
normedDistanceToCenter = math.sqrt(
|
||||
(x - width / 2) ** 2 + (y - width / 2) ** 2
|
||||
) / (math.sqrt(2) * width / 2)
|
||||
return self.interp_color(
|
||||
self.center_color, self.edge_color, normedDistanceToCenter
|
||||
)
|
||||
|
||||
|
||||
class SquareGradiantColorMask(QRColorMask):
|
||||
"""
|
||||
Fills in the foreground with a square gradient from the center to the edge
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, back_color=(255, 255, 255), center_color=(0, 0, 0), edge_color=(0, 0, 255)
|
||||
):
|
||||
self.back_color = back_color
|
||||
self.center_color = center_color
|
||||
self.edge_color = edge_color
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
normedDistanceToCenter = max(abs(x - width / 2), abs(y - width / 2)) / (
|
||||
width / 2
|
||||
)
|
||||
return self.interp_color(
|
||||
self.center_color, self.edge_color, normedDistanceToCenter
|
||||
)
|
||||
|
||||
|
||||
class HorizontalGradiantColorMask(QRColorMask):
|
||||
"""
|
||||
Fills in the foreground with a gradient sweeping from the left to the right
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, back_color=(255, 255, 255), left_color=(0, 0, 0), right_color=(0, 0, 255)
|
||||
):
|
||||
self.back_color = back_color
|
||||
self.left_color = left_color
|
||||
self.right_color = right_color
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
return self.interp_color(self.left_color, self.right_color, x / width)
|
||||
|
||||
|
||||
class VerticalGradiantColorMask(QRColorMask):
|
||||
"""
|
||||
Fills in the forefround with a gradient sweeping from the top to the bottom
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, back_color=(255, 255, 255), top_color=(0, 0, 0), bottom_color=(0, 0, 255)
|
||||
):
|
||||
self.back_color = back_color
|
||||
self.top_color = top_color
|
||||
self.bottom_color = bottom_color
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
return self.interp_color(self.top_color, self.bottom_color, y / width)
|
||||
|
||||
|
||||
class ImageColorMask(QRColorMask):
|
||||
"""
|
||||
Fills in the foreground with pixels from another image, either passed by
|
||||
path or passed by image object.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, back_color=(255, 255, 255), color_mask_path=None, color_mask_image=None
|
||||
):
|
||||
self.back_color = back_color
|
||||
if color_mask_image:
|
||||
self.color_img = color_mask_image
|
||||
else:
|
||||
self.color_img = Image.open(color_mask_path)
|
||||
|
||||
self.has_transparency = len(self.back_color) == 4
|
||||
|
||||
def initialize(self, styledPilImage, image):
|
||||
self.paint_color = styledPilImage.paint_color
|
||||
self.color_img = self.color_img.resize(image.size)
|
||||
|
||||
def get_fg_pixel(self, image, x, y):
|
||||
width, _ = image.size
|
||||
return self.color_img.getpixel((x, y))
|
||||
@@ -0,0 +1,10 @@
|
||||
# For backwards compatibility, importing the PIL drawers here.
|
||||
try:
|
||||
from .pil import CircleModuleDrawer # noqa: F401
|
||||
from .pil import GappedSquareModuleDrawer # noqa: F401
|
||||
from .pil import HorizontalBarsDrawer # noqa: F401
|
||||
from .pil import RoundedModuleDrawer # noqa: F401
|
||||
from .pil import SquareModuleDrawer # noqa: F401
|
||||
from .pil import VerticalBarsDrawer # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,36 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import abc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qrcode.image.base import BaseImage
|
||||
|
||||
|
||||
class QRModuleDrawer(abc.ABC):
|
||||
"""
|
||||
QRModuleDrawer exists to draw the modules of the QR Code onto images.
|
||||
|
||||
For this, technically all that is necessary is a ``drawrect(self, box,
|
||||
is_active)`` function which takes in the box in which it is to draw,
|
||||
whether or not the box is "active" (a module exists there). If
|
||||
``needs_neighbors`` is set to True, then the method should also accept a
|
||||
``neighbors`` kwarg (the neighboring pixels).
|
||||
|
||||
It is frequently necessary to also implement an "initialize" function to
|
||||
set up values that only the containing Image class knows about.
|
||||
|
||||
For examples of what these look like, see doc/module_drawers.png
|
||||
"""
|
||||
|
||||
needs_neighbors = False
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
def initialize(self, img: "BaseImage") -> None:
|
||||
self.img = img
|
||||
|
||||
@abc.abstractmethod
|
||||
def drawrect(self, box, is_active) -> None:
|
||||
...
|
||||
@@ -0,0 +1,266 @@
|
||||
# Needed on case-insensitive filesystems
|
||||
from __future__ import absolute_import
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from qrcode.compat.pil import Image, ImageDraw
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qrcode.image.styledpil import StyledPilImage
|
||||
from qrcode.main import ActiveWithNeighbors
|
||||
|
||||
# When drawing antialiased things, make them bigger and then shrink them down
|
||||
# to size after the geometry has been drawn.
|
||||
ANTIALIASING_FACTOR = 4
|
||||
|
||||
|
||||
class StyledPilQRModuleDrawer(QRModuleDrawer):
|
||||
"""
|
||||
A base class for StyledPilImage module drawers.
|
||||
|
||||
NOTE: the color that this draws in should be whatever is equivalent to
|
||||
black in the color space, and the specified QRColorMask will handle adding
|
||||
colors as necessary to the image
|
||||
"""
|
||||
|
||||
img: "StyledPilImage"
|
||||
|
||||
|
||||
class SquareModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules as simple squares
|
||||
"""
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.imgDraw = ImageDraw.Draw(self.img._img)
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if is_active:
|
||||
self.imgDraw.rectangle(box, fill=self.img.paint_color)
|
||||
|
||||
|
||||
class GappedSquareModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules as simple squares that are not contiguous.
|
||||
|
||||
The size_ratio determines how wide the squares are relative to the width of
|
||||
the space they are printed in
|
||||
"""
|
||||
|
||||
def __init__(self, size_ratio=0.8):
|
||||
self.size_ratio = size_ratio
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.imgDraw = ImageDraw.Draw(self.img._img)
|
||||
self.delta = (1 - self.size_ratio) * self.img.box_size / 2
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if is_active:
|
||||
smaller_box = (
|
||||
box[0][0] + self.delta,
|
||||
box[0][1] + self.delta,
|
||||
box[1][0] - self.delta,
|
||||
box[1][1] - self.delta,
|
||||
)
|
||||
self.imgDraw.rectangle(smaller_box, fill=self.img.paint_color)
|
||||
|
||||
|
||||
class CircleModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules as circles
|
||||
"""
|
||||
|
||||
circle = None
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
box_size = self.img.box_size
|
||||
fake_size = box_size * ANTIALIASING_FACTOR
|
||||
self.circle = Image.new(
|
||||
self.img.mode,
|
||||
(fake_size, fake_size),
|
||||
self.img.color_mask.back_color,
|
||||
)
|
||||
ImageDraw.Draw(self.circle).ellipse(
|
||||
(0, 0, fake_size, fake_size), fill=self.img.paint_color
|
||||
)
|
||||
self.circle = self.circle.resize((box_size, box_size), Image.Resampling.LANCZOS)
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if is_active:
|
||||
self.img._img.paste(self.circle, (box[0][0], box[0][1]))
|
||||
|
||||
|
||||
class RoundedModuleDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws the modules with all 90 degree corners replaced with rounded edges.
|
||||
|
||||
radius_ratio determines the radius of the rounded edges - a value of 1
|
||||
means that an isolated module will be drawn as a circle, while a value of 0
|
||||
means that the radius of the rounded edge will be 0 (and thus back to 90
|
||||
degrees again).
|
||||
"""
|
||||
|
||||
needs_neighbors = True
|
||||
|
||||
def __init__(self, radius_ratio=1):
|
||||
self.radius_ratio = radius_ratio
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.corner_width = int(self.img.box_size / 2)
|
||||
self.setup_corners()
|
||||
|
||||
def setup_corners(self):
|
||||
mode = self.img.mode
|
||||
back_color = self.img.color_mask.back_color
|
||||
front_color = self.img.paint_color
|
||||
self.SQUARE = Image.new(
|
||||
mode, (self.corner_width, self.corner_width), front_color
|
||||
)
|
||||
|
||||
fake_width = self.corner_width * ANTIALIASING_FACTOR
|
||||
radius = self.radius_ratio * fake_width
|
||||
diameter = radius * 2
|
||||
base = Image.new(
|
||||
mode, (fake_width, fake_width), back_color
|
||||
) # make something 4x bigger for antialiasing
|
||||
base_draw = ImageDraw.Draw(base)
|
||||
base_draw.ellipse((0, 0, diameter, diameter), fill=front_color)
|
||||
base_draw.rectangle((radius, 0, fake_width, fake_width), fill=front_color)
|
||||
base_draw.rectangle((0, radius, fake_width, fake_width), fill=front_color)
|
||||
self.NW_ROUND = base.resize(
|
||||
(self.corner_width, self.corner_width), Image.Resampling.LANCZOS
|
||||
)
|
||||
self.SW_ROUND = self.NW_ROUND.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
|
||||
self.SE_ROUND = self.NW_ROUND.transpose(Image.Transpose.ROTATE_180)
|
||||
self.NE_ROUND = self.NW_ROUND.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
|
||||
def drawrect(self, box: List[List[int]], is_active: "ActiveWithNeighbors"):
|
||||
if not is_active:
|
||||
return
|
||||
# find rounded edges
|
||||
nw_rounded = not is_active.W and not is_active.N
|
||||
ne_rounded = not is_active.N and not is_active.E
|
||||
se_rounded = not is_active.E and not is_active.S
|
||||
sw_rounded = not is_active.S and not is_active.W
|
||||
|
||||
nw = self.NW_ROUND if nw_rounded else self.SQUARE
|
||||
ne = self.NE_ROUND if ne_rounded else self.SQUARE
|
||||
se = self.SE_ROUND if se_rounded else self.SQUARE
|
||||
sw = self.SW_ROUND if sw_rounded else self.SQUARE
|
||||
self.img._img.paste(nw, (box[0][0], box[0][1]))
|
||||
self.img._img.paste(ne, (box[0][0] + self.corner_width, box[0][1]))
|
||||
self.img._img.paste(
|
||||
se, (box[0][0] + self.corner_width, box[0][1] + self.corner_width)
|
||||
)
|
||||
self.img._img.paste(sw, (box[0][0], box[0][1] + self.corner_width))
|
||||
|
||||
|
||||
class VerticalBarsDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws vertically contiguous groups of modules as long rounded rectangles,
|
||||
with gaps between neighboring bands (the size of these gaps is inversely
|
||||
proportional to the horizontal_shrink).
|
||||
"""
|
||||
|
||||
needs_neighbors = True
|
||||
|
||||
def __init__(self, horizontal_shrink=0.8):
|
||||
self.horizontal_shrink = horizontal_shrink
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.half_height = int(self.img.box_size / 2)
|
||||
self.delta = int((1 - self.horizontal_shrink) * self.half_height)
|
||||
self.setup_edges()
|
||||
|
||||
def setup_edges(self):
|
||||
mode = self.img.mode
|
||||
back_color = self.img.color_mask.back_color
|
||||
front_color = self.img.paint_color
|
||||
|
||||
height = self.half_height
|
||||
width = height * 2
|
||||
shrunken_width = int(width * self.horizontal_shrink)
|
||||
self.SQUARE = Image.new(mode, (shrunken_width, height), front_color)
|
||||
|
||||
fake_width = width * ANTIALIASING_FACTOR
|
||||
fake_height = height * ANTIALIASING_FACTOR
|
||||
base = Image.new(
|
||||
mode, (fake_width, fake_height), back_color
|
||||
) # make something 4x bigger for antialiasing
|
||||
base_draw = ImageDraw.Draw(base)
|
||||
base_draw.ellipse((0, 0, fake_width, fake_height * 2), fill=front_color)
|
||||
|
||||
self.ROUND_TOP = base.resize((shrunken_width, height), Image.Resampling.LANCZOS)
|
||||
self.ROUND_BOTTOM = self.ROUND_TOP.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
|
||||
|
||||
def drawrect(self, box, is_active: "ActiveWithNeighbors"):
|
||||
if is_active:
|
||||
# find rounded edges
|
||||
top_rounded = not is_active.N
|
||||
bottom_rounded = not is_active.S
|
||||
|
||||
top = self.ROUND_TOP if top_rounded else self.SQUARE
|
||||
bottom = self.ROUND_BOTTOM if bottom_rounded else self.SQUARE
|
||||
self.img._img.paste(top, (box[0][0] + self.delta, box[0][1]))
|
||||
self.img._img.paste(
|
||||
bottom, (box[0][0] + self.delta, box[0][1] + self.half_height)
|
||||
)
|
||||
|
||||
|
||||
class HorizontalBarsDrawer(StyledPilQRModuleDrawer):
|
||||
"""
|
||||
Draws horizontally contiguous groups of modules as long rounded rectangles,
|
||||
with gaps between neighboring bands (the size of these gaps is inversely
|
||||
proportional to the vertical_shrink).
|
||||
"""
|
||||
|
||||
needs_neighbors = True
|
||||
|
||||
def __init__(self, vertical_shrink=0.8):
|
||||
self.vertical_shrink = vertical_shrink
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
super().initialize(*args, **kwargs)
|
||||
self.half_width = int(self.img.box_size / 2)
|
||||
self.delta = int((1 - self.vertical_shrink) * self.half_width)
|
||||
self.setup_edges()
|
||||
|
||||
def setup_edges(self):
|
||||
mode = self.img.mode
|
||||
back_color = self.img.color_mask.back_color
|
||||
front_color = self.img.paint_color
|
||||
|
||||
width = self.half_width
|
||||
height = width * 2
|
||||
shrunken_height = int(height * self.vertical_shrink)
|
||||
self.SQUARE = Image.new(mode, (width, shrunken_height), front_color)
|
||||
|
||||
fake_width = width * ANTIALIASING_FACTOR
|
||||
fake_height = height * ANTIALIASING_FACTOR
|
||||
base = Image.new(
|
||||
mode, (fake_width, fake_height), back_color
|
||||
) # make something 4x bigger for antialiasing
|
||||
base_draw = ImageDraw.Draw(base)
|
||||
base_draw.ellipse((0, 0, fake_width * 2, fake_height), fill=front_color)
|
||||
|
||||
self.ROUND_LEFT = base.resize((width, shrunken_height), Image.Resampling.LANCZOS)
|
||||
self.ROUND_RIGHT = self.ROUND_LEFT.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
|
||||
def drawrect(self, box, is_active: "ActiveWithNeighbors"):
|
||||
if is_active:
|
||||
# find rounded edges
|
||||
left_rounded = not is_active.W
|
||||
right_rounded = not is_active.E
|
||||
|
||||
left = self.ROUND_LEFT if left_rounded else self.SQUARE
|
||||
right = self.ROUND_RIGHT if right_rounded else self.SQUARE
|
||||
self.img._img.paste(left, (box[0][0], box[0][1] + self.delta))
|
||||
self.img._img.paste(
|
||||
right, (box[0][0] + self.half_width, box[0][1] + self.delta)
|
||||
)
|
||||
@@ -0,0 +1,141 @@
|
||||
import abc
|
||||
from decimal import Decimal
|
||||
from typing import TYPE_CHECKING, NamedTuple
|
||||
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
from qrcode.compat.etree import ET
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qrcode.image.svg import SvgFragmentImage, SvgPathImage
|
||||
|
||||
ANTIALIASING_FACTOR = 4
|
||||
|
||||
|
||||
class Coords(NamedTuple):
|
||||
x0: Decimal
|
||||
y0: Decimal
|
||||
x1: Decimal
|
||||
y1: Decimal
|
||||
xh: Decimal
|
||||
yh: Decimal
|
||||
|
||||
|
||||
class BaseSvgQRModuleDrawer(QRModuleDrawer):
|
||||
img: "SvgFragmentImage"
|
||||
|
||||
def __init__(self, *, size_ratio: Decimal = Decimal(1), **kwargs):
|
||||
self.size_ratio = size_ratio
|
||||
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.box_delta = (1 - self.size_ratio) * self.img.box_size / 2
|
||||
self.box_size = Decimal(self.img.box_size) * self.size_ratio
|
||||
self.box_half = self.box_size / 2
|
||||
|
||||
def coords(self, box) -> Coords:
|
||||
row, col = box[0]
|
||||
x = row + self.box_delta
|
||||
y = col + self.box_delta
|
||||
|
||||
return Coords(
|
||||
x,
|
||||
y,
|
||||
x + self.box_size,
|
||||
y + self.box_size,
|
||||
x + self.box_half,
|
||||
y + self.box_half,
|
||||
)
|
||||
|
||||
|
||||
class SvgQRModuleDrawer(BaseSvgQRModuleDrawer):
|
||||
tag = "rect"
|
||||
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.tag_qname = ET.QName(self.img._SVG_namespace, self.tag)
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if not is_active:
|
||||
return
|
||||
self.img._img.append(self.el(box))
|
||||
|
||||
@abc.abstractmethod
|
||||
def el(self, box):
|
||||
...
|
||||
|
||||
|
||||
class SvgSquareDrawer(SvgQRModuleDrawer):
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.unit_size = self.img.units(self.box_size)
|
||||
|
||||
def el(self, box):
|
||||
coords = self.coords(box)
|
||||
return ET.Element(
|
||||
self.tag_qname, # type: ignore
|
||||
x=self.img.units(coords.x0),
|
||||
y=self.img.units(coords.y0),
|
||||
width=self.unit_size,
|
||||
height=self.unit_size,
|
||||
)
|
||||
|
||||
|
||||
class SvgCircleDrawer(SvgQRModuleDrawer):
|
||||
tag = "circle"
|
||||
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
self.radius = self.img.units(self.box_half)
|
||||
|
||||
def el(self, box):
|
||||
coords = self.coords(box)
|
||||
return ET.Element(
|
||||
self.tag_qname, # type: ignore
|
||||
cx=self.img.units(coords.xh),
|
||||
cy=self.img.units(coords.yh),
|
||||
r=self.radius,
|
||||
)
|
||||
|
||||
|
||||
class SvgPathQRModuleDrawer(BaseSvgQRModuleDrawer):
|
||||
img: "SvgPathImage"
|
||||
|
||||
def drawrect(self, box, is_active: bool):
|
||||
if not is_active:
|
||||
return
|
||||
self.img._subpaths.append(self.subpath(box))
|
||||
|
||||
@abc.abstractmethod
|
||||
def subpath(self, box) -> str:
|
||||
...
|
||||
|
||||
|
||||
class SvgPathSquareDrawer(SvgPathQRModuleDrawer):
|
||||
def subpath(self, box) -> str:
|
||||
coords = self.coords(box)
|
||||
x0 = self.img.units(coords.x0, text=False)
|
||||
y0 = self.img.units(coords.y0, text=False)
|
||||
x1 = self.img.units(coords.x1, text=False)
|
||||
y1 = self.img.units(coords.y1, text=False)
|
||||
|
||||
return f"M{x0},{y0}H{x1}V{y1}H{x0}z"
|
||||
|
||||
|
||||
class SvgPathCircleDrawer(SvgPathQRModuleDrawer):
|
||||
def initialize(self, *args, **kwargs) -> None:
|
||||
super().initialize(*args, **kwargs)
|
||||
|
||||
def subpath(self, box) -> str:
|
||||
coords = self.coords(box)
|
||||
x0 = self.img.units(coords.x0, text=False)
|
||||
yh = self.img.units(coords.yh, text=False)
|
||||
h = self.img.units(self.box_half - self.box_delta, text=False)
|
||||
x1 = self.img.units(coords.x1, text=False)
|
||||
|
||||
# rx,ry is the centerpoint of the arc
|
||||
# 1? is the x-axis-rotation
|
||||
# 2? is the large-arc-flag
|
||||
# 3? is the sweep flag
|
||||
# x,y is the point the arc is drawn to
|
||||
|
||||
return f"M{x0},{yh}A{h},{h} 0 0 0 {x1},{yh}A{h},{h} 0 0 0 {x0},{yh}z"
|
||||
179
Backend/venv/lib/python3.12/site-packages/qrcode/image/svg.py
Normal file
179
Backend/venv/lib/python3.12/site-packages/qrcode/image/svg.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import decimal
|
||||
from decimal import Decimal
|
||||
from typing import List, Optional, Type, Union, overload
|
||||
|
||||
from typing_extensions import Literal
|
||||
|
||||
import qrcode.image.base
|
||||
from qrcode.compat.etree import ET
|
||||
from qrcode.image.styles.moduledrawers import svg as svg_drawers
|
||||
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
|
||||
|
||||
|
||||
class SvgFragmentImage(qrcode.image.base.BaseImageWithDrawer):
|
||||
"""
|
||||
SVG image builder
|
||||
|
||||
Creates a QR-code image as a SVG document fragment.
|
||||
"""
|
||||
|
||||
_SVG_namespace = "http://www.w3.org/2000/svg"
|
||||
kind = "SVG"
|
||||
allowed_kinds = ("SVG",)
|
||||
default_drawer_class: Type[QRModuleDrawer] = svg_drawers.SvgSquareDrawer
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
ET.register_namespace("svg", self._SVG_namespace)
|
||||
super().__init__(*args, **kwargs)
|
||||
# Save the unit size, for example the default box_size of 10 is '1mm'.
|
||||
self.unit_size = self.units(self.box_size)
|
||||
|
||||
@overload
|
||||
def units(self, pixels: Union[int, Decimal], text: Literal[False]) -> Decimal:
|
||||
...
|
||||
|
||||
@overload
|
||||
def units(self, pixels: Union[int, Decimal], text: Literal[True] = True) -> str:
|
||||
...
|
||||
|
||||
def units(self, pixels, text=True):
|
||||
"""
|
||||
A box_size of 10 (default) equals 1mm.
|
||||
"""
|
||||
units = Decimal(pixels) / 10
|
||||
if not text:
|
||||
return units
|
||||
units = units.quantize(Decimal("0.001"))
|
||||
context = decimal.Context(traps=[decimal.Inexact])
|
||||
try:
|
||||
for d in (Decimal("0.01"), Decimal("0.1"), Decimal("0")):
|
||||
units = units.quantize(d, context=context)
|
||||
except decimal.Inexact:
|
||||
pass
|
||||
return f"{units}mm"
|
||||
|
||||
def save(self, stream, kind=None):
|
||||
self.check_kind(kind=kind)
|
||||
self._write(stream)
|
||||
|
||||
def to_string(self, **kwargs):
|
||||
return ET.tostring(self._img, **kwargs)
|
||||
|
||||
def new_image(self, **kwargs):
|
||||
return self._svg(**kwargs)
|
||||
|
||||
def _svg(self, tag=None, version="1.1", **kwargs):
|
||||
if tag is None:
|
||||
tag = ET.QName(self._SVG_namespace, "svg")
|
||||
dimension = self.units(self.pixel_size)
|
||||
return ET.Element(
|
||||
tag, # type: ignore
|
||||
width=dimension,
|
||||
height=dimension,
|
||||
version=version,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def _write(self, stream):
|
||||
ET.ElementTree(self._img).write(stream, xml_declaration=False)
|
||||
|
||||
|
||||
class SvgImage(SvgFragmentImage):
|
||||
"""
|
||||
Standalone SVG image builder
|
||||
|
||||
Creates a QR-code image as a standalone SVG document.
|
||||
"""
|
||||
|
||||
background: Optional[str] = None
|
||||
drawer_aliases: qrcode.image.base.DrawerAliases = {
|
||||
"circle": (svg_drawers.SvgCircleDrawer, {}),
|
||||
"gapped-circle": (svg_drawers.SvgCircleDrawer, {"size_ratio": Decimal(0.8)}),
|
||||
"gapped-square": (svg_drawers.SvgSquareDrawer, {"size_ratio": Decimal(0.8)}),
|
||||
}
|
||||
|
||||
def _svg(self, tag="svg", **kwargs):
|
||||
svg = super()._svg(tag=tag, **kwargs)
|
||||
svg.set("xmlns", self._SVG_namespace)
|
||||
if self.background:
|
||||
svg.append(
|
||||
ET.Element(
|
||||
"rect",
|
||||
fill=self.background,
|
||||
x="0",
|
||||
y="0",
|
||||
width="100%",
|
||||
height="100%",
|
||||
)
|
||||
)
|
||||
return svg
|
||||
|
||||
def _write(self, stream):
|
||||
ET.ElementTree(self._img).write(stream, encoding="UTF-8", xml_declaration=True)
|
||||
|
||||
|
||||
class SvgPathImage(SvgImage):
|
||||
"""
|
||||
SVG image builder with one single <path> element (removes white spaces
|
||||
between individual QR points).
|
||||
"""
|
||||
|
||||
QR_PATH_STYLE = {
|
||||
"fill": "#000000",
|
||||
"fill-opacity": "1",
|
||||
"fill-rule": "nonzero",
|
||||
"stroke": "none",
|
||||
}
|
||||
|
||||
needs_processing = True
|
||||
path: Optional[ET.Element] = None
|
||||
default_drawer_class: Type[QRModuleDrawer] = svg_drawers.SvgPathSquareDrawer
|
||||
drawer_aliases = {
|
||||
"circle": (svg_drawers.SvgPathCircleDrawer, {}),
|
||||
"gapped-circle": (
|
||||
svg_drawers.SvgPathCircleDrawer,
|
||||
{"size_ratio": Decimal(0.8)},
|
||||
),
|
||||
"gapped-square": (
|
||||
svg_drawers.SvgPathSquareDrawer,
|
||||
{"size_ratio": Decimal(0.8)},
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._subpaths: List[str] = []
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def _svg(self, viewBox=None, **kwargs):
|
||||
if viewBox is None:
|
||||
dimension = self.units(self.pixel_size, text=False)
|
||||
viewBox = "0 0 {d} {d}".format(d=dimension)
|
||||
return super()._svg(viewBox=viewBox, **kwargs)
|
||||
|
||||
def process(self):
|
||||
# Store the path just in case someone wants to use it again or in some
|
||||
# unique way.
|
||||
self.path = ET.Element(
|
||||
ET.QName("path"), # type: ignore
|
||||
d="".join(self._subpaths),
|
||||
id="qr-path",
|
||||
**self.QR_PATH_STYLE,
|
||||
)
|
||||
self._subpaths = []
|
||||
self._img.append(self.path)
|
||||
|
||||
|
||||
class SvgFillImage(SvgImage):
|
||||
"""
|
||||
An SvgImage that fills the background to white.
|
||||
"""
|
||||
|
||||
background = "white"
|
||||
|
||||
|
||||
class SvgPathFillImage(SvgPathImage):
|
||||
"""
|
||||
An SvgPathImage that fills the background to white.
|
||||
"""
|
||||
|
||||
background = "white"
|
||||
Reference in New Issue
Block a user