Skip to content
Advertisement

Store Binary Data in QR Code (ZXING Java Library)

My Java program needs to send a binary payload via QR Code, but I can’t get it to work. I have tried several QR Code libraries and many approaches, but all seem to have this problem. My current implementation uses ZXING.

The problem is that all the Java libraries I’ve tried seem to be focused on String payloads, and do not provide support for binary data. The common suggested solution to this is to encode the binary data as Base64. However, my data is already near the size limit of QR Codes. With the 4x inflation caused by Base64 encoding, my data is far too big. I have already expended significant effort into reducing the size of the payload, and it currently consists of 4 character hashes delimited by new lines; all inside max level compression by the Java Deflator class. I can’t make it any smaller.

I need a way to store binary data in a QR code with minimal data inflation overhead.

Advertisement

Answer

Update: I recently went back and published the referenced code as a project on GitHub for anyone who wants to use it. https://github.com/yurelle/Base45Encoder


I developed a solution which only introduces a storage efficiency loss of -8%. It exploits a built-in compression optimization of the ZXING QR Code Library.

Explanation

ZXING will automatically detect if your String payload is purely AlphaNumeric (by their own definition), and if so, it will automatically compress 2 AlphaNumeric characters into 11 bits. The definition ZXING uses for “alphanumeric” is all-caps only, 0-9, and a few special symbols (‘/’, ‘:’, etc.). All told, their definition allows 45 possible values. Then, it packs 2 of these Base45 digits into 11 bits.

2 digits in base 45 is 2,025 possible values. 11 bits has a maximum storage capacity of 2,048 possible states. This is only a loss of 1.1% in storage efficiency behind raw binary.

JavaScript

However, this is the ideal / theoretical efficiency. My implementation processes data in chunks, using a Long as a computational buffer. However, since Java Long’s are singed, we can only use the lower 7 bytes. The conversion code requires continuously positive values; using the highest 8th byte would contaminate the sign bit and randomly produce negative values.

Real-World Test:

Using a 7 byte Long to encode a 2KB buffer of random bytes, we get the following results.

JavaScript

This is a real-world storage efficiency loss of only 8%.

JavaScript

Solution

I implemented it as a self-contained static utility class, so all you have to do is call:

JavaScript

Alternatively, you can also do it via InputStreams:

JavaScript

Here’s the implementation

JavaScript

Here are some tests I ran to verify the code:

JavaScript
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement