web-dev-qa-db-fra.com

Encodage et décodage QR Code à l'aide de zxing

D'accord, je vais donc prendre le risque que quelqu'un ici ait déjà utilisé le zxing. Je développe une application Java, et l'une des choses qu'il doit faire est de coder un tableau d'octets de données dans un code QR, puis de le décoder ultérieurement.

Voici un exemple de l'apparence de mon encodeur:

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);

Le tableau d'octets de début dans ce code est juste utilisé pour le tester. Les données d'octets réelles seront modifiées.

Voici à quoi ressemble mon décodeur:

//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) est une méthode qui convertit un tableau d'octets en une chaîne de caractères hexadécimaux.

Lorsque j'essaie d'exécuter ces deux blocs de code ensemble, voici la sortie:

48454c4c4f
202b0b78cc00ec11ec11ec11ec11ec11ec11ec

De toute évidence, le texte est codé, mais les octets de données réels sont complètement désactivés. Toute aide serait appréciée ici.

35
LandonSchropp

Donc, pour référence future pour quiconque ne veut pas passer deux jours à chercher sur Internet pour comprendre cela, lorsque vous encodez des tableaux d'octets en codes QR, vous devez utiliser le ISO-8859-1ensemble de caractères, pas UTF-8.

49
LandonSchropp

ceci est mon exemple de travail Java pour coder le code QR en utilisant ZXing avec l'encodage UTF-8, veuillez noter: vous devrez changer le chemin et les données utf8 en vos caractères de chemin et de langue

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());
    }
}

}
18
Shaybc

Pour ce que ça vaut, mon pic groovy semble fonctionner avec les encodages de caractères UTF-8 et ISO-8859-1. Je ne sais pas ce qui se passera lorsqu'un décodeur non zxing essaie de décoder l'image encodée UTF-8, bien que ... varie probablement en fonction de l'appareil.

// ------------------------------------------------------------------------------------
// 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()        
    }

}
5
klassek

Peut-être la peine de regarder QRGen , qui est construit sur ZXing et prend en charge UTF-8 avec ce type de syntaxe:

// 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();
2
lucrussell

Si vous avez vraiment besoin de coder UTF-8, vous pouvez essayer d'ajouter la marque d'ordre d'octet unicode. Je n'ai aucune idée de l'étendue du support de cette méthode, mais ZXing semble au moins le soutenir: http://code.google.com/p/zxing/issues/detail?id=1

J'ai lu récemment sur le mode QR, et je pense que j'ai vu la même pratique mentionnée ailleurs, mais je ne suis pas le plus brumeux où .

1
perennialmind

J'ai essayé d'utiliser ISO-8859-1 comme indiqué dans la première réponse. Tout s'est bien passé sur le codage, mais quand j'ai essayé d'obtenir l'octet [] en utilisant la chaîne de résultat sur le décodage, tous les octets négatifs sont devenus le caractère 63 (point d'interrogation). Le code suivant ne fonctionne pas:

// 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;

Cela semble si étrange parce que l'API dans une très ancienne version (je ne sais pas exactement) avait une méthode qui fonctionne bien:

Vector byteSegments = result.getByteSegments();

J'ai donc essayé de chercher pourquoi cette méthode a été supprimée et j'ai réalisé qu'il y avait un moyen d'obtenir ByteSegments, via des métadonnées. Donc ma méthode de décodage ressemble à:

// 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;
0
BrunoJCM