lincolnloop / python-qrcode

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

Unable to embed an image into a vcard #331

Open macmichael01 opened 1 year ago

macmichael01 commented 1 year ago

I am getting an error when trying to embed an image into a vcard whether that be by link or base64 encode. Here is the source code:

import qrcode
import base64
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer
from qrcode.image.styles.colormasks import (
  RadialGradiantColorMask,
  HorizontalGradiantColorMask,
  VerticalGradiantColorMask,
  SquareGradiantColorMask,
)

contact_info = {
    "name": "Joe Doe",
    "email": "jon@doe.com"
    "phone": "+1234567890",
    "organization": "Joe Doe Inc",
    "memo": "Message",
    "url": "https://joedoe.it",
    "photo": "/path/to/image/joedoe.jpg",
    # "photo_uri": "https://joedoe.com/path/to/image.jpg",
}

vcard = f"BEGIN:VCARD\n" \
        f"VERSION:3.0\n" \
        f"N:{contact_info['name']}\n" \
        f"EMAIL:{contact_info['email']}\n" \
        f"TEL:{contact_info['phone']}\n" \
        f"ORG:{contact_info['organization']}\n" \
        f"NOTE:{contact_info['memo']}\n" \
        f"URL:{contact_info['url']}\n"

if 'photo' in contact_info:
    with open(contact_info['photo'], 'rb') as photo_file:
        photo_data = base64.b64encode(photo_file.read())
    vcard += f"PHOTO;ENCODING=b;TYPE=JPEG:{photo_data.decode('utf-8')}\n"

if 'photo_uri' in contact_info:
    photo_uri = contact_info['photo_uri']
    vcard += f"PHOTO;TYPE=JPEG;VALUE=URI:{photo_uri}\n"

vcard += f"END:VCARD"

qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_H,
    box_size=10,
    border=4,
)
qr.add_data(vcard)
qr.make(fit=True)
image = qr.make_image(
  image_factory=StyledPilImage,
  back_color="white",
  module_drawer=RoundedModuleDrawer(),
  color_mask=RadialGradiantColorMask(back_color=(255, 255, 255), center_color=(122, 31, 161), edge_color=(81, 45, 168)),
  # color_mask=VerticalGradiantColorMask(back_color=(255, 255, 255), top_color=(122, 31, 161), bottom_color=(81, 45, 168))
  # color_mask=SquareGradiantColorMask(back_color=(255, 255, 255), center_color=(122, 31, 161), edge_color=(81, 45, 168))
  # color_mask=HorizontalGradiantColorMask(left_color=(122, 31, 161), right_color=(81, 45, 168))
)
image.save("image.png")

Here is the error which happens after image = qr.make_image(...):

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/lib/python3.10/site-packages/qrcode/main.py", line 353, in make_image
    self.make()
  File "/opt/homebrew/lib/python3.10/site-packages/qrcode/main.py", line 160, in make
    self.best_fit(start=self.version)
  File "/opt/homebrew/lib/python3.10/site-packages/qrcode/main.py", line 232, in best_fit
    self.version = bisect_left(
  File "/opt/homebrew/lib/python3.10/site-packages/qrcode/main.py", line 115, in version
    util.check_version(value)
  File "/opt/homebrew/lib/python3.10/site-packages/qrcode/util.py", line 185, in check_version
    raise ValueError(f"Invalid version (was {version}, expected 1 to 40)")
ValueError: Invalid version (was 41, expected 1 to 40)

I even tried to make a vcard in Mac OS Contacts App with an image embded and paste the string value within the card file and I got the same error. When I strip out the photo part of the vcard, the qrcode generates without error.

egh commented 1 year ago

You are trying to put too much data in your qr code https://en.wikipedia.org/wiki/QR_code#Information_capacity

macmichael01 commented 1 year ago

That seems plausible, although checking the character length, I found was 1171 which seems to be under the max size limit.

macmichael01 commented 1 year ago

I commented out the line: error_correction=qrcode.constants.ERROR_CORRECT_H, and the qrcode generated. I am also able to use ERROR_CORRECT_L I'm guessing that error correction adds some parity bits that exceed the max allowed characters in a QRCode.

It would be nice there was a better error message indicating what happened.