From 69497a79352e94c961bb4f8e4ad51395a48ba749 Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Wed, 11 Dec 2024 17:14:09 -0800 Subject: [PATCH] Accept data: URIs on the command-line --- test/test_all.py | 13 +++++++++++++ zxing/__init__.py | 20 +++++++++++++++++++- zxing/__main__.py | 10 ++++++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/test/test_all.py b/test/test_all.py index 9f6b159..e934264 100644 --- a/test/test_all.py +++ b/test/test_all.py @@ -187,3 +187,16 @@ def test_bad_file_format_error(): global test_reader with helper.assertRaises(zxing.BarCodeReaderException): test_reader.decode(os.path.join(test_barcode_dir, 'bad_format.png')) + + +def test_data_uris(): + def _check_data_uri(uri, contents, suffix): + fobj = zxing.data_uri_to_fobj(uri) + assert fobj.getvalue() == contents + assert fobj.name.endswith(suffix) + + yield from ((_check_data_uri, uri, contents, suffix) for (uri, contents, suffix) in ( + ('data:image/png,ABCD', b'ABCD', '.png'), + ('data:image/jpeg;base64,3q2+7w==', bytes.fromhex('deadbeef'), '.jpeg'), + ('data:application/binary,%f1%f2%f3', bytes.fromhex('f1f2f3'), '.binary'), + )) diff --git a/zxing/__init__.py b/zxing/__init__.py index ad1ad1f..93bd6db 100644 --- a/zxing/__init__.py +++ b/zxing/__init__.py @@ -14,8 +14,9 @@ import sys import urllib.parse import zipfile +from base64 import b64decode from enum import Enum -from io import IOBase +from io import BytesIO, IOBase from itertools import chain try: @@ -34,6 +35,23 @@ def file_uri_to_path(s): raise ValueError(uri) return urllib.parse.unquote_plus(uri.path) + +def data_uri_to_fobj(s): + r = urllib.parse.urlparse(s) + if r.scheme == 'data' and not r.netloc: + mime, *rest = r.path.split(',', 1) + if rest: + if mime.endswith(';base64') and rest: + mime = mime[:-7] + data = b64decode(rest[0]) + else: + data = urllib.parse.unquote_to_bytes(rest[0]) + ff = BytesIO(data) + ff.name = f'data_uri_{len(data)}_bytes.{mime.split("/")[-1]}' + return ff + raise ValueError("Cannot handle URIs other than data:MIMETYPE[;base64],DATA") + + class BarCodeReaderException(Exception): def __init__(self, message, filename=None): self.message, self.filename = message, filename diff --git a/zxing/__main__.py b/zxing/__main__.py index 3ca99a7..417e8dd 100644 --- a/zxing/__main__.py +++ b/zxing/__main__.py @@ -2,7 +2,7 @@ import csv from sys import stdout, stdin -from . import BarCodeReader, BarCodeReaderException +from . import BarCodeReader, BarCodeReaderException, data_uri_to_fobj from .version import __version__ @@ -24,7 +24,7 @@ def main(): p.add_argument('-c', '--csv', action='store_true') p.add_argument('--try-harder', action='store_true') p.add_argument('--pure-barcode', action='store_true') - p.add_argument('image', nargs='+') + p.add_argument('image', nargs='+', help='File path or data: URI of an image containing a barcode') p.add_argument('-P', '--classpath', help=argparse.SUPPRESS) p.add_argument('-J', '--java', help=argparse.SUPPRESS) p.add_argument('-V', '--version', action='store_true') @@ -46,6 +46,12 @@ def main(): if fn == '-': ff = stdin.buffer fn = ff.name + elif ':' in fn: + try: + ff = data_uri_to_fobj(fn) + fn = ff.name + except ValueError as exc: + p.error(exc.args[0]) else: ff = fn