lincolnloop / python-qrcode

Python QR Code image generator
https://pypi.python.org/pypi/qrcode
Other
4.25k stars 660 forks source link

Should raise exception if using embedded image with incorrect error correction? #363

Open turicas opened 2 months ago

turicas commented 2 months ago

I cannot read the generated qrcode when an embedded image is specified and error_correction is different from qrcode.constants.ERROR_CORRECT_H. I think this happens because the image obstructs a good portion of the qrcode and the error corrections smaller than ERROR_CORRECT_H are not enough to recover the information from this obstructed part.

You can try to scan the following images (the data on all of them is the URL https://brasil.io/ - check the code below). I was able to read only the last image (tried on Motorola Edge 20 Lite, Nothing phone/Android 14, iPhone SE iOS 17.4, Xiaomi Redmi 9 and Xiaomi Poco X3 Pro).

qrcode.constants.ERROR_CORRECT_L:

qrcode-brasilio-L

qrcode.constants.ERROR_CORRECT_M:

qrcode-brasilio-M

qrcode.constants.ERROR_CORRECT_Q:

qrcode-brasilio-Q

qrcode.constants.ERROR_CORRECT_H:

qrcode-brasilio-H

Following is the code to generate the images above:

from urllib.request import urlopen
from pathlib import Path

import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import GappedSquareModuleDrawer, SquareModuleDrawer

# QRCode definitions
drawer = GappedSquareModuleDrawer()
eye_drawer = SquareModuleDrawer()
url = "https://brasil.io/"
center_image_url = "https://raw.githubusercontent.com/turicas/brasil.io/develop/branding/logo/logo-brasilio.png"
center_image_path = Path("logo-brasilio.png")
background_color = "white"
fill_color = "black"
if not center_image_path.exists():
    print("Downloading center image")
    response = urlopen(center_image_url)
    with center_image_path.open(mode="wb") as fobj:
        fobj.write(response.read())

# Test code
error_corrections = {
    "L": qrcode.constants.ERROR_CORRECT_L,
    "M": qrcode.constants.ERROR_CORRECT_M,
    "Q": qrcode.constants.ERROR_CORRECT_Q,
    "H": qrcode.constants.ERROR_CORRECT_H,
}
for error_correction, error_correction_constant in error_corrections.items():
    print(f"Generating qrcode for error correction {error_correction}")
    qr = qrcode.QRCode(
        version=1,
        error_correction=error_correction_constant,
        box_size=10,
        border=4,
    )
    qr.add_data(url)
    qr.make(fit=True)
    img = qr.make_image(
        image_factory=StyledPilImage,
        back_color=background_color,
        embeded_image_path=str(center_image_path.absolute()),
        fill_color=fill_color,
        module_drawer=drawer,
        eye_drawer=eye_drawer,
    )
    img.save(f"qrcode-brasilio-{error_correction}.png")

If I'm not doing anything wrong, then I'd suggest the best behaviour to be raising an exception if embeded_image_path is set and error_correction is different from qrcode.constants.ERROR_CORRECT_H. I'm willing to create a PR fixing this if you approve this fix idea.