bitsy-wallet/PDFJet/src/main/java/com/pdfjet/QRCode.java

410 lines
12 KiB
Java

/**
*
Copyright (c) 2009 Kazuhiko Arase
URL: http://www.d-project.com/
Licensed under the MIT license:
http://www.opensource.org/licenses/mit-license.php
The word "QR Code" is registered trademark of
DENSO WAVE INCORPORATED
http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
package com.pdfjet;
import java.io.UnsupportedEncodingException;
/**
* Used to create 2D QR Code barcodes. Please see Example_20.
*
* @author Kazuhiko Arase
*/
public class QRCode implements Drawable {
private static final int PAD0 = 0xEC;
private static final int PAD1 = 0x11;
private Boolean[][] modules;
private int moduleCount = 33; // Magic Number
private int errorCorrectLevel = ErrorCorrectLevel.M;
private float x;
private float y;
private byte[] qrData;
private float m1 = 2.0f; // Module length
/**
* Used to create 2D QR Code barcodes.
*
* @param str the string to encode.
* @param errorCorrectLevel the desired error correction level.
* @throws UnsupportedEncodingException
*/
public QRCode(String str, int errorCorrectLevel) throws UnsupportedEncodingException {
this.qrData = str.getBytes("UTF-8");
this.errorCorrectLevel = errorCorrectLevel;
this.make(false, getBestMaskPattern());
}
/**
* Sets the position where this barcode will be drawn on the page.
*
* @param x the x coordinate of the top left corner of the barcode.
* @param y the y coordinate of the top left corner of the barcode.
*/
public void setPosition(double x, double y) {
setPosition((float) x, (float) y);
}
/**
* Sets the position where this barcode will be drawn on the page.
*
* @param x the x coordinate of the top left corner of the barcode.
* @param y the y coordinate of the top left corner of the barcode.
*/
public void setPosition(float x, float y) {
setLocation(x, y);
}
/**
* Sets the location where this barcode will be drawn on the page.
*
* @param x the x coordinate of the top left corner of the barcode.
* @param y the y coordinate of the top left corner of the barcode.
*/
public void setLocation(float x, float y) {
this.x = x;
this.y = y;
}
/**
* Sets the module length of this barcode.
* The default value is 2.0f
*
* @param moduleLength the specified module length.
*/
public void setModuleLength(double moduleLength) {
this.m1 = (float) moduleLength;
}
/**
* Sets the module length of this barcode.
* The default value is 2.0f
*
* @param moduleLength the specified module length.
*/
public void setModuleLength(float moduleLength) {
this.m1 = moduleLength;
}
/**
* Draws this barcode on the specified page.
*
* @param page the specified page.
* @return x and y coordinates of the bottom right corner of this component.
* @throws Exception
*/
public float[] drawOn(Page page) throws Exception {
for (int row = 0; row < modules.length; row++) {
for (int col = 0; col < modules.length; col++) {
if (isDark(row, col)) {
page.fillRect(x + col*m1, y + row*m1, m1, m1);
}
}
}
float w = m1*modules.length;
float h = m1*modules.length;
return new float[] {x + w, y + h};
}
public Boolean[][] getData() {
return modules;
}
/**
* @param row the row.
* @param col the column.
*/
protected boolean isDark(int row, int col) {
if (modules[row][col] != null) {
return modules[row][col];
}
else {
return false;
}
}
protected int getModuleCount() {
return moduleCount;
}
protected int getBestMaskPattern() {
int minLostPoint = 0;
int pattern = 0;
for (int i = 0; i < 8; i++) {
make(true, i);
int lostPoint = QRUtil.getLostPoint(this);
if (i == 0 || minLostPoint > lostPoint) {
minLostPoint = lostPoint;
pattern = i;
}
}
return pattern;
}
protected void make(boolean test, int maskPattern) {
modules = new Boolean[moduleCount][moduleCount];
setupPositionProbePattern(0, 0);
setupPositionProbePattern(moduleCount - 7, 0);
setupPositionProbePattern(0, moduleCount - 7);
setupPositionAdjustPattern();
setupTimingPattern();
setupTypeInfo(test, maskPattern);
mapData(createData(errorCorrectLevel), maskPattern);
}
private void mapData(byte[] data, int maskPattern) {
int inc = -1;
int row = moduleCount - 1;
int bitIndex = 7;
int byteIndex = 0;
for (int col = moduleCount - 1; col > 0; col -= 2) {
if (col == 6) col--;
while (true) {
for (int c = 0; c < 2; c++) {
if (modules[row][col - c] == null) {
boolean dark = false;
if (byteIndex < data.length) {
dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
}
boolean mask = QRUtil.getMask(maskPattern, row, col - c);
if (mask) {
dark = !dark;
}
modules[row][col - c] = dark;
bitIndex--;
if (bitIndex == -1) {
byteIndex++;
bitIndex = 7;
}
}
}
row += inc;
if (row < 0 || moduleCount <= row) {
row -= inc;
inc = -inc;
break;
}
}
}
}
private void setupPositionAdjustPattern() {
int[] pos = {6, 26}; // Magic Numbers
for (int i = 0; i < pos.length; i++) {
for (int j = 0; j < pos.length; j++) {
int row = pos[i];
int col = pos[j];
if (modules[row][col] != null) {
continue;
}
for (int r = -2; r <= 2; r++) {
for (int c = -2; c <= 2; c++) {
modules[row + r][col + c] =
r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0);
}
}
}
}
}
private void setupPositionProbePattern(int row, int col) {
for (int r = -1; r <= 7; r++) {
for (int c = -1; c <= 7; c++) {
if (row + r <= -1 || moduleCount <= row + r
|| col + c <= -1 || moduleCount <= col + c) {
continue;
}
modules[row + r][col + c] =
(0 <= r && r <= 6 && (c == 0 || c == 6)) ||
(0 <= c && c <= 6 && (r == 0 || r == 6)) ||
(2 <= r && r <= 4 && 2 <= c && c <= 4);
}
}
}
private void setupTimingPattern() {
for (int r = 8; r < moduleCount - 8; r++) {
if (modules[r][6] != null) {
continue;
}
modules[r][6] = (r % 2 == 0);
}
for (int c = 8; c < moduleCount - 8; c++) {
if (modules[6][c] != null) {
continue;
}
modules[6][c] = (c % 2 == 0);
}
}
private void setupTypeInfo(boolean test, int maskPattern) {
int data = (errorCorrectLevel << 3) | maskPattern;
int bits = QRUtil.getBCHTypeInfo(data);
for (int i = 0; i < 15; i++) {
Boolean mod = (!test && ((bits >> i) & 1) == 1);
if (i < 6) {
modules[i][8] = mod;
}
else if (i < 8) {
modules[i + 1][8] = mod;
}
else {
modules[moduleCount - 15 + i][8] = mod;
}
}
for (int i = 0; i < 15; i++) {
Boolean mod = (!test && ((bits >> i) & 1) == 1);
if (i < 8) {
modules[8][moduleCount - i - 1] = mod;
}
else if (i < 9) {
modules[8][15 - i - 1 + 1] = mod;
}
else {
modules[8][15 - i - 1] = mod;
}
}
modules[moduleCount - 8][8] = !test;
}
private byte[] createData(int errorCorrectLevel) {
RSBlock[] rsBlocks = RSBlock.getRSBlocks(errorCorrectLevel);
BitBuffer buffer = new BitBuffer();
buffer.put(4, 4);
buffer.put(qrData.length, 8);
for (int i = 0; i < qrData.length; i++) {
buffer.put(qrData[i], 8);
}
int totalDataCount = 0;
for (int i = 0; i < rsBlocks.length; i++) {
totalDataCount += rsBlocks[i].getDataCount();
}
if (buffer.getLengthInBits() > totalDataCount * 8) {
throw new IllegalArgumentException("String length overflow. ("
+ buffer.getLengthInBits()
+ ">"
+ totalDataCount * 8
+ ")");
}
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
buffer.put(0, 4);
}
// padding
while (buffer.getLengthInBits() % 8 != 0) {
buffer.put(false);
}
// padding
while (true) {
if (buffer.getLengthInBits() >= totalDataCount * 8) {
break;
}
buffer.put(PAD0, 8);
if (buffer.getLengthInBits() >= totalDataCount * 8) {
break;
}
buffer.put(PAD1, 8);
}
return createBytes(buffer, rsBlocks);
}
private byte[] createBytes(BitBuffer buffer, RSBlock[] rsBlocks) {
int offset = 0;
int maxDcCount = 0;
int maxEcCount = 0;
int[][] dcdata = new int[rsBlocks.length][];
int[][] ecdata = new int[rsBlocks.length][];
for (int r = 0; r < rsBlocks.length; r++) {
int dcCount = rsBlocks[r].getDataCount();
int ecCount = rsBlocks[r].getTotalCount() - dcCount;
maxDcCount = Math.max(maxDcCount, dcCount);
maxEcCount = Math.max(maxEcCount, ecCount);
dcdata[r] = new int[dcCount];
for (int i = 0; i < dcdata[r].length; i++) {
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
}
offset += dcCount;
Polynomial rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
Polynomial rawPoly = new Polynomial(dcdata[r], rsPoly.getLength() - 1);
Polynomial modPoly = rawPoly.mod(rsPoly);
ecdata[r] = new int[rsPoly.getLength() - 1];
for (int i = 0; i < ecdata[r].length; i++) {
int modIndex = i + modPoly.getLength() - ecdata[r].length;
ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
}
}
int totalCodeCount = 0;
for (int i = 0; i < rsBlocks.length; i++) {
totalCodeCount += rsBlocks[i].getTotalCount();
}
byte[] data = new byte[totalCodeCount];
int index = 0;
for (int i = 0; i < maxDcCount; i++) {
for (int r = 0; r < rsBlocks.length; r++) {
if (i < dcdata[r].length) {
data[index++] = (byte) dcdata[r][i];
}
}
}
for (int i = 0; i < maxEcCount; i++) {
for (int r = 0; r < rsBlocks.length; r++) {
if (i < ecdata[r].length) {
data[index++] = (byte) ecdata[r][i];
}
}
}
return data;
}
}