Playing around with QR Codes
May 11th, 2010 by Micha KopsSometimes QR codes are a nice way to distribute information like calendar events, contact information, e-mail, geo-locations or internet addresses.
In the following article we’re going to encode information to QR code images using the ZXing library and afterwards decode information from a given QR code.
Finally we’re taking a look on online QR code generators and how to integrate the ZXing library in a Maven project.
Contents
The ZXing Library
- Download the ZXing Libraries from http://code.google.com/p/zxing/downloads/list – the file name is ZXing-<version>.zip
- Unpack the downloaded archive somewhere
- Change to the extracted directory and run ant. If you don’t have JavaME installed – and you don’t have to for the samples below – run ant buildwithoutj2me – that will do the job
- Having compiled the libraries you’re now free to include the core.jar from zxing-<version>/core/ and the javase.jar from zxing-<version>/javase as dependency in your project
Example
- Main.java
package com.hascode.tutorial.qr_code; import java.io.File; public class Main { public static void main(String... args) { File imageFile = new File("/tmp/qr-code.png"); String input = "Visit hascode.com ;)"; // creating a qr code new QRCodeGenerator().generateQRcode(imageFile, input); // decoding the qr code from image String decoded = new QRCodeDecoder().decode(imageFile); System.out.println(decoded); } }
- QRCodeGenerator.java
package com.hascode.tutorial.qr_code; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.ByteMatrix; import com.google.zxing.qrcode.QRCodeWriter; public class QRCodeGenerator { private int width = 400; private int height = 400; public void generateQRcode(File imageFile, String input) { ByteMatrix mtx = null; QRCodeWriter writer = new QRCodeWriter(); try { mtx = writer.encode(input, BarcodeFormat.QR_CODE, width, height); } catch (WriterException e) { return; } if (mtx != null) { BufferedImage image = MatrixToImageWriter.toBufferedImage(mtx); try { ImageIO.write(image, "png", imageFile); } catch (IOException e) { return; } } } }
- QRCodeDecoder.java
package com.hascode.tutorial.qr_code; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import com.google.zxing.BinaryBitmap; import com.google.zxing.LuminanceSource; import com.google.zxing.Reader; import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.QRCodeReader; public class QRCodeDecoder { public String decode(File imageFile) { BufferedImage image; try { image = ImageIO.read(imageFile); } catch (IOException e1) { return "io outch"; } // creating luminance source LuminanceSource lumSource = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(lumSource)); // barcode decoding Reader reader = new QRCodeReader(); Result result = null; try { result = reader.decode(bitmap); } catch (ReaderException e) { return "reader error"; } return result.getText(); } }
Running the example you should notice the generated png image in /tmp or the directory set – if you’ve got an android smart-phone you can test the image using this free app. The generated image should look something like this:
Legal Notice
QR Code is a registered trademark of Denso Wave Inc.
Troubleshooting
- BUILD FAILED .. /usr/local/WTK2.5.2/lib not found. – run ant buildwithoutj2me instead of ant
- If you encode byte arrays into QR codes you need to use ISO-8859-1 encoding instead of UTF-8 as workaround
- If you’re getting a com.google.zxing.NotFoundException take a closer look at the produced QR code image – perhaps you have fallen victim to a strange bug in the matrix implementation in the ZXing library that produces an inverted image (black is white, white is black). If you wish to comment the bug – head over to the project’s bugtracker. If you’ve got an inverted image you need to invert the colors – using RescaleOp and RescaleOp.filter() didn’t work for me – so I coded some ugly workaround – all suggestions welcome :)
package com.hascode.tutorial.qr_code; [..] public class QRCodeGenerator { [..] public void generateQRcode(File imageFile, String input) { [..] if (mtx != null) { BufferedImage image = MatrixToImageWriter.toBufferedImage(mtx); image = invertImage(image); [..] } } private BufferedImage invertImage(BufferedImage image) { for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int rgb = image.getRGB(x, y); if (rgb == -16777216) { image.setRGB(x, y, -1); } else { image.setRGB(x, y, -16777216); } } } return image; } }
- Here is a screenshot of the inverted image from the samples above
*update*
Eric kindly noted that it is much easier to convert the bytes from the byte matrix directly – hey there are only two byte values to replace – so I have created this update to the QRCodeGenerator class:
package com.hascode.tutorial.qr_code; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.ByteMatrix; import com.google.zxing.qrcode.QRCodeWriter; public class QRCodeGenerator { private int width = 400; private int height = 400; public void generateQRcode(File imageFile, String input) { ByteMatrix mtx = null; QRCodeWriter writer = new QRCodeWriter(); try { mtx = writer.encode(input, BarcodeFormat.QR_CODE, width, height); } catch (WriterException e) { return; } if (mtx != null) { invertMatrix(mtx); BufferedImage image = MatrixToImageWriter.toBufferedImage(mtx); try { ImageIO.write(image, "png", imageFile); } catch (IOException e) { return; } } } private void invertMatrix(ByteMatrix mtx) { for (int w = 0; w < mtx.getWidth(); w++) { for (int h = 0; h < mtx.getHeight(); h++) { byte inverted = (byte) ((mtx.get(w, h) == 0x00) ? 0xFF : 0x00); mtx.set(w, h, inverted); } } } }
Online Generators for QR Codes
If you want to create a QR code fast without coding there are some nice websites that help you creating the desired QR code images – e.g. the online generator from the ZXing project itself or Google Chart that offers support for QR codes.
There is also an online decoder offered from the ZXing project where you’re able to upload and test generated files.
Maven Repository
It’s a pity that zxing won’t be published on the central Maven repository – for discussion and more information please take a look at Issue 88.
Luckily for us Adam Gent helps us out by adding the libraries to his own repository as described in his article “Adding zxing to my own Maven repository“.
This allows us to add the dependencies needed by adding the following repository and dependencies to your Maven project’s pom.xml
<repositories> <repository> <id>mvn-adamgent</id> <url>http://mvn-adamgent.googlecode.com/svn/maven/release</url> <name>Adam Gent Maven Repository</name> </repository> </repositories> <dependencies> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>1.6</version> </dependency> </dependencies>
Resources
- ZXing Online QR Code Generator
- ZXing Project Homepage
- ZXing Online Decoder
- Google Chart Tools: QR Codes
- Barcode Scanner for Android
- Wikipedia: QR Code
- Open Source QR Code Library Project
- ZXing API Javadocs
- JavaSE RescaleOp JavaDocs
- ZXing Issues: Inverted Image Bug
- 2DCode.co.uk
- Adam Gent: Adding zxing to my own Maven repository
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
public class QRCodeGenerator {
private int width = 400;
private int height = 400;
public void generateQRcode(File imageFile, String input) {
ByteMatrix mtx = null;
QRCodeWriter writer = new QRCodeWriter();
try {
mtx = writer.encode(input, BarcodeFormat.QR_CODE, width, height);
} catch (WriterException e) {
return;
}
if (mtx != null) {
invertMatrix(mtx);
BufferedImage image = MatrixToImageWriter.toBufferedImage(mtx);
try {
ImageIO.write(image, “png”, imageFile);
} catch (IOException e) {
return;
}
}
}
private void invertMatrix(ByteMatrix mtx) {
for (int w = 0; w < mtx.getWidth(); w++) {
for (int h = 0; h < mtx.getHeight(); h++) {
byte inverted = (byte) ((mtx.get(w, h) == 0×00) ? 0xFF : 0×00);
mtx.set(w, h, inverted);
}
}
}
}
Tags: 2d, Android, barcode, generator, qr code, scanner, Snippet, tutorial, zxing
May 24th, 2010 at 10:02 pm
I ran into the inversion problem, too, and found it a bit simpler to re-invert the ByteMatrix returned by the Writer (only having to deal with 0×0 and 0xff).
May 25th, 2010 at 4:02 pm
Thanks for the remark! It is indeed simpler to invert the byte matrix .. I have updated the article with a corresponding example
October 7th, 2011 at 7:57 pm
Looking for elegant solution to decode an inverse color scheme in an ECC200 2D data matrix. Inverse schemes are used widely in my community. I’m using ZXing Image Decoder API to get raw bytes. Works well with standard scheme. Not with inverse. Have read several comments on different issues. Your’s seems most cogent. Ideas?
May 5th, 2014 at 4:13 pm
I tried to generate calendar event qr code using Google ZXing API but the image generated by it is not recognisable as an event. I used the same data format specified at different websites but it only creates qr code but not as a recognizable event. BEGIN:VEVENT SUMMARY:Concert DTSTART:20110912 DTEND:20110912 DESCRIPTION:Metallica concert END:VEVENT Can you please tell me what I am missing. Only for calendar event.
May 7th, 2014 at 3:36 pm
Something like this did not work?
BEGIN:VEVENT
SUMMARY:Test Event
DTSTART:20140507T223400Z
DTEND:20140508T233400Z
END:VEVENT
You may use the zxing online generator here:
http://zxing.appspot.com/generator/