Ответ 1
Итак, для будущей ссылки для тех, кто не хочет тратить два дня на поиск в Интернете, чтобы понять это, когда вы кодируете байт-массивы в QR-коды, вы должны использовать набор символов ISO-8859-1
, а не UTF-8
.
Хорошо, так что я собираюсь взять шанс, что кто-то здесь раньше использовал 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);
Начальный массив байтов в этом коде просто используется для его проверки. Фактические байтовые данные будут меняться.
Вот как выглядит мой декодер:
//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)
- это метод, который преобразует массив байтов в строку шестнадцатеричных символов.
Когда я пытаюсь запустить эти два блока кода вместе, это вывод:
48454c4c4f
202b0b78cc00ec11ec11ec11ec11ec11ec11ec
Ясно, что текст кодируется, но фактические байты данных полностью отключены. Любая помощь будет оценена здесь.
Итак, для будущей ссылки для тех, кто не хочет тратить два дня на поиск в Интернете, чтобы понять это, когда вы кодируете байт-массивы в QR-коды, вы должны использовать набор символов ISO-8859-1
, а не UTF-8
.
это мой рабочий пример Java-кода для кодирования QR-кода с использованием ZXing с кодировкой UTF-8, обратите внимание: вам нужно будет изменить путь и данные utf8 на ваши дорожки и символы языка
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());
}
}
}
Для чего стоит мой groovy -пик, похоже, работает как с кодировками символов 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, вы можете попробовать добавить символ юникодного байта. Я не знаю, насколько широко поддерживается поддержка этого метода, но ZXing, по крайней мере, поддерживает его: http://code.google.com/p/zxing/issues/detail?id=103
Недавно я читал QR-код, и я думаю, что видел ту же самую практику, о которой упоминалось в другом месте, но я не самый тупой.
Возможно стоит посмотреть QRGen, который построен поверх ZXing и поддерживает 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, как сказано в первом ответе. Все обошлось в кодировке, но когда я попытался получить байт [], используя результирующую строку при декодировании, все отрицательные байты стали символом 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 в очень старой версии (точно не знаю) использовал метод thar хорошо:
Vector byteSegments = result.getByteSegments();
Итак, я попытался найти, почему этот метод был удален, и понял, что есть способ получить 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;