diff --git a/README.md b/README.md index 7c37330..2533f9d 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,116 @@ -# QRare +# Binary QR Converter -## Store files into QR Codes and decode them later! +A simple CLI tool to convert binary files to QR codes and back. -### Now with gzip compression and fallback to qrtools! +## Features -This Python module provides functionality to encode arbitrary binary data into a series of QR codes and decode them back into the original data. It uses the `qrcode` library for encoding and the `zxing` and `qrtools` libraries for decoding. +- Convert any binary file to a series of QR codes +- Decode QR codes back to the original binary file +- Compression to reduce the number of QR codes needed +- Error correction for reliable decoding +- File integrity verification using SHA-256 hashing +- Cross-platform compatibility (Windows, macOS, Linux) -## Installation +## Prerequisites -### Using pip +- Python 3.6 or higher +- For decoding QR codes, you need to install the ZBar library: + - On Ubuntu/Debian: `sudo apt-get install libzbar0` + - On macOS: `brew install zbar` + - On Windows: Download from [SourceForge](https://sourceforge.net/projects/zbar/) -Pubished at https://pypi.org/project/qrare/ +## Setup -`pip install qrare` +1. Clone or download this repository +2. Install dependencies: + ``` + pip install -r requirements.txt + ``` +3. Make the script executable (Linux/macOS): + ``` + chmod +x binary_qr.py + ``` -### From this repository +## Usage + +### Encoding a binary file to QR codes ``` -git clone https://github.com/tcsenpai/qrare/ - -cd qrare +python binary_qr.py encode path/to/file.bin -o output_directory ``` -#### Install dependencies +Options: +- `-o, --output-dir`: Directory to save QR code images (default: ./qrcodes) +- `-c, --chunk-size`: Size of binary chunks in bytes (default: 1024) +- `-v, --qr-version`: QR code version (1-40, higher means more capacity) (default: 40) +- `-z, --compression-level`: Zlib compression level (0-9) (default: 9) -- Python 3.x -- qrcode -- qrtools -- compress -- Pillow (PIL) -- zxing - -Install the required libraries using pip: - -`pip install qrcode pillow zxing qrtools compress` - -Or install the dependencies using: - -`pip install -r requirements.txt` - -## Functions & Usage - -### bin_to_qr(data, chunk_size=100, filename_prefix="qr_code") - -Encodes binary data into a series of QR code images. - -Parameters: -- `data` (bytes): The binary data to encode. -- `chunk_size` (int, optional): The maximum number of hexadecimal characters per QR code. Default is 100. -- `filename_prefix` (str, optional): The prefix for the generated QR code image filenames. Default is "qr_code". - -This function performs the following steps: -1. Compresses the binary data using gzip. -2. Converts the binary data to a hexadecimal string. -3. Splits the hex string into chunks of the specified size. -4. Creates a QR code for each chunk, including chunk number and total chunk count. -5. Saves each QR code as a PNG image. - -### qr_to_bin(filename_prefix="qr_code") - -Decodes a series of QR code images back into the original binary data. - -Parameters: -- `filename_prefix` (str, optional): The prefix of the QR code image filenames to decode. Default is "qr_code". - -Returns: -- `bytes`: The decoded binary data, or `None` if no QR codes were found. - -This function performs the following steps: -1. Iterates through numbered QR code images with the given prefix. -2. Decodes each QR code using the zxing library, falling back to qrtools if zxing cannot decode a qrcode. -3. Extracts chunk information and data from each decoded QR code. -4. Reconstructs the original hexadecimal string from the chunks. -5. Converts the hexadecimal string back to binary data. -6. Decompresses the binary data using gzip. -7. Returns the decompressed binary data. - -## Usage Example - -```python - -# Encode binary data into QR codes -import qrare - -original_data = b"This is a binary string \x00\x01\x02 with some non-printable characters. -qrare.bin_to_qr(original_data, chunk_size=50) -``` - -And vice versa - -```python -# Decode QR codes back into binary data -import qrare - -decoded_data = qrare.qr_to_bin() -print("Decoded data:", decoded_data) -print("Original and decoded data match:", original_data == decoded_data) +### Decoding QR codes back to a binary file +``` +python binary_qr.py decode path/to/qrcodes/*.png -o output_directory ``` -## Notes +or -- The script automatically determines the appropriate QR code version based on the data size. -- Data is compressed using gzip before being encoded into QR codes to maximize data capacity. -- Data is then decompressed using gzip after being decoded from QR codes. -- Error correction level is set to LOW (L) to maximize data capacity. -- The script handles binary data, including non-printable characters. -- THe script has a fallback method that (until now) allows for a 100% error free decoding. -- QR code images are saved and read from the current working directory. -- Ensure you have write permissions in the directory where the script is run. +``` +python binary_qr.py decode path/to/qrcodes/ -o output_directory +``` -## Error Handling +Options: +- `-o, --output-dir`: Directory to save the reconstructed file (default: ./output) -- The script includes basic error handling for file not found and decoding errors. -- If an error occurs during decoding, it will be printed to the console. +### Show version information -## Limitations +``` +python binary_qr.py version +``` -- The maximum data capacity depends on the QR code version and error correction level. -- Very large binary files may require a large number of QR codes. -- The script assumes that QR codes will be scanned in the correct order for decoding. +## Examples + +### Encode a PDF file to QR codes + +``` +python binary_qr.py encode document.pdf -o qrcodes/ +``` + +### Decode QR codes back to the original PDF + +``` +python binary_qr.py decode qrcodes/ -o recovered/ +``` + +### Run the example script + +``` +python example.py +``` + +## How it works + +1. The binary file is compressed using zlib to reduce size +2. The compressed data is split into chunks +3. Each chunk is encoded with metadata (chunk index, total chunks, filename, file hash) +4. Each chunk with metadata is converted to a QR code image +5. When decoding, the QR codes are read, and the chunks are reassembled +6. The reassembled data is decompressed to recover the original file +7. The file hash is verified to ensure data integrity + +## Tips for optimal use + +- **Adjust chunk size**: For larger files, increasing the chunk size (`-c` option) can reduce the number of QR codes but may make them more complex and harder to scan. +- **QR code version**: Higher versions (up to 40) can store more data but require higher resolution scanning. +- **Compression level**: The default level (9) provides maximum compression but takes longer. Lower values (1-8) are faster but produce more QR codes. +- **Printing QR codes**: When printing, ensure high quality and sufficient size for reliable scanning. +- **Scanning**: Use a good camera in well-lit conditions for best results when scanning QR codes. + +## Troubleshooting + +- **Missing ZBar library**: If you get an error about missing ZBar, install it using the instructions in the Prerequisites section. +- **QR code decoding fails**: Make sure all QR codes are clearly visible and not damaged. Try increasing the error correction level. +- **File too large**: Try increasing the chunk size or QR code version to reduce the number of QR codes. +- **Invalid QR version**: Make sure to use a QR version between 1 and 40. + +## License + +MIT License \ No newline at end of file diff --git a/example_with_files.py b/example_with_files.py deleted file mode 100644 index 56ddc34..0000000 --- a/example_with_files.py +++ /dev/null @@ -1,11 +0,0 @@ -import qrare - -with open("pika.webp", "rb") as f: - data = f.read() - -qrare.bin_to_qr(data, chunk_size=1000, box_size=10, border=4) - -test_file = qrare.qr_to_bin() - -with open("pika_out.webp", "wb") as f: - f.write(test_file) \ No newline at end of file diff --git a/example_with_string.py b/example_with_string.py deleted file mode 100644 index e9fa3e8..0000000 --- a/example_with_string.py +++ /dev/null @@ -1,9 +0,0 @@ -import qrare - -# Example usage -original_data = b"This is a binary string \x00\x01\x02 with some non-printable characters." -qrare.bin_to_qr(original_data, chunk_size=50) - -decoded_data = qrare.qr_to_bin() -print("Decoded data:", decoded_data) -print("Original and decoded data match:", original_data == decoded_data) \ No newline at end of file diff --git a/input_test_file.txt b/input_test_file.txt deleted file mode 100644 index 9b7c9c9..0000000 --- a/input_test_file.txt +++ /dev/null @@ -1,5 +0,0 @@ -This file can be used (and is used) as an example input file for the `example_with_files.py` script. - -Feel free to modify it as needed. - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut purus eget sapien. \ No newline at end of file diff --git a/pika.webp b/pika.webp deleted file mode 100644 index ba82749..0000000 Binary files a/pika.webp and /dev/null differ diff --git a/qrare.py b/qrare.py deleted file mode 100644 index 5ff0aac..0000000 --- a/qrare.py +++ /dev/null @@ -1,82 +0,0 @@ -import qrcode -import zxing -import qrtools -from compress import Compressor - -def bin_to_qr(data, chunk_size=100, filename_prefix="qr_code", box_size=10, border=4): - compressed_data = compress_bytes(data) - hex_data = compressed_data.hex() - chunks = [hex_data[i:i+chunk_size] for i in range(0, len(hex_data), chunk_size)] - total_chunks = len(chunks) - - for i, chunk in enumerate(chunks, 1): - qr = qrcode.QRCode(version=None, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=box_size, border=border) - qr.add_data(f"{i}/{total_chunks}:{chunk}") - qr.make(fit=True) - print(f"Generating QR code {i}", end="") - img = qr.make_image(fill_color="black", back_color="white") - img.save(f"{filename_prefix}_{i}.png") - print(f"[OK]") - - print(f"Generated {total_chunks} QR codes.") - print(f"Each QR code except the last one will contain {chunk_size} bytes of data.") - print(f"The last QR code will contain the remaining bytes of data ({len(chunks[-1] )} bytes).") - -def qr_to_bin(filename_prefix="qr_code"): - chunks = {} - i = 1 - reader = zxing.BarCodeReader() - fallback_reader = qrtools.QR() - - while True: - try: - filename = f"{filename_prefix}_{i}.png" - print(f"Decoding QR code {i}...", end="") - barcode = reader.decode(filename) - print(f"decoded...", end="") - if barcode and barcode.parsed: - decoded = barcode.parsed - else: - print(f"Could not decode QR code {i}: {barcode.raw} with zxing, trying fallback reader...") - fallback_reader.decode(filename) - decoded = fallback_reader.data - # split the decoded string into chunk_info and chunk_data - chunk_info, chunk_data = decoded.split(':', 1) - chunk_num, total_chunks = map(int, chunk_info.split('/')) - chunks[chunk_num] = chunk_data - # print(chunk_data) - print(f"binary data extracted [OK]") - if chunk_num == total_chunks: - break - # print(fallback_reader.data) - if not chunks[i]: - print(f"Error decoding QR code {i}: {barcode.raw}") - exit(-1) - print(f"binary data extracted [OK]") - - i += 1 - except FileNotFoundError: - break - except Exception as e: - print(f"Error decoding QR code {i}: {e}") - exit(-1) - - if not chunks: - print("No QR codes found.") - return None - - hex_data_compressed = ''.join(chunks[i] for i in range(1, len(chunks) + 1)) - bytes_compressed = bytes.fromhex(hex_data_compressed) - return decompress_bytes(bytes_compressed) - -# Compressing as much as possible - -def compress_bytes(data): - compressor = Compressor() - compressor.use_gzip() - return compressor.compress(data) - -def decompress_bytes(data): - compressor = Compressor() - compressor.use_gzip() - return compressor.decompress(data) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e2b2768..caba2fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ -qrcode -Pillow -zxing -qrtools -compress \ No newline at end of file +# Core dependencies +qrcode>=7.0 +pillow>=8.0.0 +pyzbar>=0.1.8 \ No newline at end of file