以下程序使用ZXing(和PDFBox)将字节数组(长度N = 1,2,3,...)编码为QR码,然后将其嵌入到PDF文档中,渲染,提取为BufferedImage,并解码。比较解码和编码的字节数组。首先尝试解码而不使用解码选项PURE_BARCODE,如果失败,则尝试使用PURE_BARCODE选项进行解码。
解码开始在N = 20附近失败(对于示例随机种子,N = 28)。请注意,在解码失败之前,字节数组正确解码。
解码失败时,会在对话框中显示失败的QR码,并将有效负载字符串打印到控制台。
如果我将手机指向此对话框,我的手机没有问题解码此QR码图像,并且解码后的字符串与控制台上的字符串匹配。
我犯了什么错误?
package zxing;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.Map;
import java.util.PrimitiveIterator;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
public class Problem {
public static void main(String[] args) throws IOException,
WriterException,
NotFoundException,
ChecksumException,
FormatException {
Random r = new Random(12345);
PrimitiveIterator.OfInt rb = r.ints(-128, 127).iterator();
for (int N = 1; N < 100; N += 1) { // encode and decode random byte arrays of size N
System.out.println(N);
for (int numRuns = 0; numRuns < 10; numRuns++) { // number of tries at size N
byte[] payload = new byte[N]; // payload to be encoded
for (int i = 0; i < N; i++) { // payload random initialization
payload[i] = rb.next().byteValue();
}
final String payloadString = new String(payload, STRING_ENCODING); // encode as string
// encode using zxing
final BufferedImage qr = MatrixToImageWriter.toBufferedImage(new QRCodeWriter().encode(payloadString,
BarcodeFormat.QR_CODE, 256, 256, ENCODING_HINTS));
// insert into PDF
PDDocument pdDocument = new PDDocument();
PDPage page = new PDPage();
pdDocument.addPage(page);
PDPageContentStream pageContent = new PDPageContentStream(pdDocument, page);
pageContent.drawImage(JPEGFactory.createFromImage(pdDocument, qr), 0, 0, 200, 200);
pageContent.close();
// render pdf and extract qr code image
BufferedImage pageImage = new PDFRenderer(pdDocument).renderImage(0);
BufferedImage qrcodeImage = pageImage.getSubimage(0, pageImage.getHeight() - 200, 200, 200);
pdDocument.close();
byte[] resultPayload;
try { // try zxing decode qrcodeImage *not* in PURE_BARCODE mode
resultPayload = new QRCodeReader().decode(
new BinaryBitmap(new HybridBinarizer(new RGBLuminanceSource(qrcodeImage.getWidth(),
qrcodeImage.getHeight(),
qrcodeImage.getRGB(0, 0, qrcodeImage.getWidth(), qrcodeImage.getHeight(), null, 0,
qrcodeImage.getWidth())))), DECODING_HINTS_IMPURE).getText().getBytes(
STRING_ENCODING);
} catch (Throwable ex) {
try { // failed so try zxing decode qrcodeImage in PURE_BARCODE mode
resultPayload = new QRCodeReader().decode(
new BinaryBitmap(new HybridBinarizer(new RGBLuminanceSource(qrcodeImage.getWidth(),
qrcodeImage.getHeight(),
qrcodeImage.getRGB(0, 0, qrcodeImage.getWidth(), qrcodeImage.getHeight(), null,
0,
qrcodeImage.getWidth())))), DECODING_HINTS_PURE).getText().getBytes(
STRING_ENCODING);
} catch (Throwable ex2) { // both decodings failed, display image
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(qrcodeImage)),
"N=" + Integer.toString(N),
JOptionPane.PLAIN_MESSAGE, null);
System.out.println("Encoded=" + payloadString);
throw ex2;
}
}
if (!Arrays.equals(payload, resultPayload)) {
throw new RuntimeException("Payload mismatch.");
}
}
}
}
private final static String STRING_ENCODING = "ISO-8859-1";
final private static Map<EncodeHintType, Object> ENCODING_HINTS = new EnumMap<EncodeHintType, Object>(
EncodeHintType.class);
static {
ENCODING_HINTS.put(EncodeHintType.CHARACTER_SET, STRING_ENCODING);
ENCODING_HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
}
final private static Map<DecodeHintType, Object> DECODING_HINTS_IMPURE = new EnumMap<DecodeHintType, Object>(
DecodeHintType.class);
static {
DECODING_HINTS_IMPURE.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
DECODING_HINTS_IMPURE.put(DecodeHintType.CHARACTER_SET, STRING_ENCODING);
}
final private static Map<DecodeHintType, Object> DECODING_HINTS_PURE = new EnumMap<DecodeHintType, Object>(
DecodeHintType.class);
static {
DECODING_HINTS_PURE.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
DECODING_HINTS_PURE.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
DECODING_HINTS_PURE.put(DecodeHintType.CHARACTER_SET, STRING_ENCODING);
}
}
[... ZXing抛出FormatException ...]
[... ZXing手机应用程序也可以读取QR码对话框...]
答案 0 :(得分:1)
以下更改将精度提高到大约N = 64,用于高质量纠错,N = 100,无纠错(满足我的需要)。
需要进行三项更改。
首先,正如@Tilman Hausherr所建议的,在将QR码图像插入PDF时使用无损工厂。虽然这对OP中提供的代码没有任何影响,但它是其他更改生效的必要条件。
此外,以二进制而不是rgb呈现PDF,并以更高比例呈现PDF。默认比例(1.0)仅为72dpi。
pageContent.drawImage(LosslessFactory.createFromImage(pdDocument, qr), 0, 0, 200, 200);
...
BufferedImage pageImage = new PDFRenderer(pdDocument)
.renderImage(0,3.0f,ImageType.BINARY);