QR码编码和解码使用zxing
好的,所以我要借此机会在这里有人使用zxing。 我正在开发一个Java应用程序,它需要做的事情之一就是将一个字节的数据数组编码成QR码,然后在稍后解码。
下面是我的编码器的例子:
byte[] b = {0x48, 0x45, 0x4C, 0x4C, 0x4F}; //convert the byte array into a UTF-8 string String data; try { data = new String(b, "UTF8"); } catch (UnsupportedEncodingException e) { //the program shouldn't be able to get here return; } //get a byte matrix for the data ByteMatrix matrix; com.google.zxing.Writer writer = new QRCodeWriter(); try { matrix = writer.encode(data, com.google.zxing.BarcodeFormat.QR_CODE, width, height); } catch (com.google.zxing.WriterException e) { //exit the method return; } //generate an image from the byte matrix int width = matrix.getWidth(); int height = matrix.getHeight(); byte[][] array = matrix.getArray(); //create buffered image to draw to BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //iterate through the matrix and draw the pixels to the image for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int grayValue = array[y][x] & 0xff; image.setRGB(x, y, (grayValue == 0 ? 0 : 0xFFFFFF)); } } //write the image to the output stream ImageIO.write(image, "png", outputStream);
这个代码中的起始字节数组只是用来testing它。 实际的字节数据将会变化。
这是我的解码器看起来像:
//get the data from the input stream BufferedImage image = ImageIO.read(inputStream); //convert the image to a binary bitmap source LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); //decode the barcode QRCodeReader reader = new QRCodeReader(); Result result; try { result = reader.decode(bitmap, hints); } catch (ReaderException e) { //the data is improperly formatted throw new MCCDatabaseMismatchException(); } byte[] b = result.getRawBytes(); System.out.println(ByteHelper.convertUnsignedBytesToHexString(result.getText().getBytes("UTF8"))); System.out.println(ByteHelper.convertUnsignedBytesToHexString(b));
convertUnsignedBytesToHexString(byte)
是一种转换hexstring中字节数组的方法。
当我尝试运行这两个代码块在一起,这是输出:
48454c4c4f 202b0b78cc00ec11ec11ec11ec11ec11ec11ec
显然文本正在编码,但实际的数据字节是完全closures的。 任何帮助将不胜感激。
因此,对于任何不想花费两天时间search互联网的人来说,如果将字节数组编码为QR码,则必须使用ISO-8859-1
字符集,而不是UTF-8
。
这是我的工作示例Java代码,使用ZXing UTF-8编码对QR码进行编码,请注意:您需要将path和utf8数据更改为path和语言字符
package com.mypackage.qr; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.Hashtable; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.*; public class CreateQR { public static void main(String[] args) { Charset charset = Charset.forName("UTF-8"); CharsetEncoder encoder = charset.newEncoder(); byte[] b = null; try { // Convert a string to UTF-8 bytes in a ByteBuffer ByteBuffer bbuf = encoder.encode(CharBuffer.wrap("utf 8 characters - i used hebrew, but you should write some of your own language characters")); b = bbuf.array(); } catch (CharacterCodingException e) { System.out.println(e.getMessage()); } String data; try { data = new String(b, "UTF-8"); // get a byte matrix for the data BitMatrix matrix = null; int h = 100; int w = 100; com.google.zxing.Writer writer = new MultiFormatWriter(); try { Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>(2); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); matrix = writer.encode(data, com.google.zxing.BarcodeFormat.QR_CODE, w, h, hints); } catch (com.google.zxing.WriterException e) { System.out.println(e.getMessage()); } // change this path to match yours (this is my mac home folder, you can use: c:\\qr_png.png if you are on windows) String filePath = "/Users/shaybc/Desktop/OutlookQR/qr_png.png"; File file = new File(filePath); try { MatrixToImageWriter.writeToFile(matrix, "PNG", file); System.out.println("printing to " + file.getAbsolutePath()); } catch (IOException e) { System.out.println(e.getMessage()); } } catch (UnsupportedEncodingException e) { System.out.println(e.getMessage()); } } }
对于它的价值,我的常规高峰似乎可以同时使用UTF-8和ISO-8859-1字符编码。 不知道当一个非zxing解码器试图解码UTF-8编码图像时会发生什么,虽然…可能会有所不同,取决于设备。
// ------------------------------------------------------------------------------------ // Requires: groovy-1.7.6, jdk1.6.0_03, ./lib with zxing core-1.7.jar, javase-1.7.jar // Javadocs: http://zxing.org/w/docs/javadoc/overview-summary.html // Run with: groovy -cp "./lib/*" zxing.groovy // ------------------------------------------------------------------------------------ import com.google.zxing.* import com.google.zxing.common.* import com.google.zxing.client.j2se.* import java.awt.image.BufferedImage import javax.imageio.ImageIO def class zxing { def static main(def args) { def filename = "./qrcode.png" def data = "This is a test to see if I can encode and decode this data..." def charset = "UTF-8" //"ISO-8859-1" def hints = new Hashtable<EncodeHintType, String>([(EncodeHintType.CHARACTER_SET): charset]) writeQrCode(filename, data, charset, hints, 100, 100) assert data == readQrCode(filename, charset, hints) } def static writeQrCode(def filename, def data, def charset, def hints, def width, def height) { BitMatrix matrix = new MultiFormatWriter().encode(new String(data.getBytes(charset), charset), BarcodeFormat.QR_CODE, width, height, hints) MatrixToImageWriter.writeToFile(matrix, filename.substring(filename.lastIndexOf('.')+1), new File(filename)) } def static readQrCode(def filename, def charset, def hints) { BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(ImageIO.read(new FileInputStream(filename))))) Result result = new MultiFormatReader().decode(binaryBitmap, hints) result.getText() } }
如果您确实需要编码UTF-8,则可以尝试预先添加unicode字节顺序标记。 我不知道这种方法的支持有多广泛,但ZXing至less似乎支持它: http : //code.google.com/p/zxing/issues/detail? id=103
最近我一直在阅读QR模式,我想我已经看到了其他地方提到的相同的做法,但我并没有在哪里模糊。
也许值得一提的是在ZXing之上构build的QRGen ,并且支持UTF-8语法:
// if using special characters don't forget to supply the encoding VCard johnSpecial = new VCard("Jöhn Dɵe") .setAdress("ëåäöƞ Sträät 1, 1234 Döestüwn"); QRCode.from(johnSpecial).withCharset("UTF-8").file();
我试着用第一个答案中所说的ISO-8859-1。 在编码上一切正常,但是当我尝试在解码时使用结果string获取字节[]时,所有负数字节都变成了字符63(问号)。 以下代码不起作用:
// Encoding works great byte[] contents = new byte[]{-1}; QRCodeWriter codeWriter = new QRCodeWriter(); BitMatrix bitMatrix = codeWriter.encode(new String(contents, Charset.forName("ISO-8859-1")), BarcodeFormat.QR_CODE, w, h); // Decodes like this fails LuminanceSource ls = new BufferedImageLuminanceSource(encodedBufferedImage); Result result = new QRCodeReader().decode(new BinaryBitmap( new HybridBinarizer(ls))); byte[] resultBytes = result.getText().getBytes(Charset.forName("ISO-8859-1")); // a byte[] with byte 63 is given return resultBytes;
它看起来很奇怪,因为API在一个非常旧的版本(不知道确切)有一个方法可以很好地工作:
Vector byteSegments = result.getByteSegments();
所以我试图search为什么这个方法被删除,并意识到有一种方法可以通过元数据来获取ByteSegments。 所以我的解码方法如下所示:
// Decodes like this works perfectly LuminanceSource ls = new BufferedImageLuminanceSource(encodedBufferedImage); Result result = new QRCodeReader().decode(new BinaryBitmap( new HybridBinarizer(ls))); Vector byteSegments = (Vector) result.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS); int i = 0; int tam = 0; for (Object o : byteSegments) { byte[] bs = (byte[])o; tam += bs.length; } byte[] resultBytes = new byte[tam]; i = 0; for (Object o : byteSegments) { byte[] bs = (byte[])o; for (byte b : bs) { resultBytes[i++] = b; } } return resultBytes;