Fix placement of ECI segment, which was wrong. Add test. Try to clean up the legacy C++ port a bit more.

git-svn-id: https://zxing.googlecode.com/svn/trunk@2342 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2012-07-06 21:57:21 +00:00
parent 99011addb3
commit c73aac2f90
7 changed files with 162 additions and 341 deletions

View file

@ -72,8 +72,7 @@ public final class QRCodeWriter implements Writer {
}
}
QRCode code = new QRCode();
Encoder.encode(contents, errorCorrectionLevel, hints, code);
QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
return renderResult(code, width, height);
}

View file

@ -55,12 +55,10 @@ public final class Encoder {
// The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
// Basically it applies four rules and summate all penalties.
private static int calculateMaskPenalty(ByteMatrix matrix) {
int penalty = 0;
penalty += MaskUtil.applyMaskPenaltyRule1(matrix);
penalty += MaskUtil.applyMaskPenaltyRule2(matrix);
penalty += MaskUtil.applyMaskPenaltyRule3(matrix);
penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
return penalty;
return MaskUtil.applyMaskPenaltyRule1(matrix)
+ MaskUtil.applyMaskPenaltyRule2(matrix)
+ MaskUtil.applyMaskPenaltyRule3(matrix)
+ MaskUtil.applyMaskPenaltyRule4(matrix);
}
/**
@ -74,69 +72,88 @@ public final class Encoder {
* Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
* with which clients can specify the encoding mode. For now, we don't need the functionality.
*/
public static void encode(String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)
throws WriterException {
encode(content, ecLevel, null, qrCode);
public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException {
return encode(content, ecLevel, null);
}
public static void encode(String content,
ErrorCorrectionLevel ecLevel,
Map<EncodeHintType,?> hints,
QRCode qrCode) throws WriterException {
public static QRCode encode(String content,
ErrorCorrectionLevel ecLevel,
Map<EncodeHintType,?> hints) throws WriterException {
// Determine what character encoding has been specified by the caller, if any
String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
if (encoding == null) {
encoding = DEFAULT_BYTE_MODE_ENCODING;
}
// Step 1: Choose the mode (encoding).
// Pick an encoding mode appropriate for the content. Note that this will not attempt to use
// multiple modes / segments even if that were more efficient. Twould be nice.
Mode mode = chooseMode(content, encoding);
BitArray dataBits = new BitArray();
// This will store the header information, like mode and
// length, as well as "header" segments like an ECI segment.
BitArray headerBits = new BitArray();
// Step 1.5: Append ECI message if applicable
// Append ECI segment if applicable
if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
if (eci != null) {
appendECI(eci, dataBits);
appendECI(eci, headerBits);
}
}
// Step 2: Append "bytes" into "dataBits" in appropriate encoding.
// (With ECI in place,) Write the mode marker
appendModeInfo(mode, headerBits);
// Collect data within the main segment, separately, to count its size if needed. Don't add it to
// main payload yet.
BitArray dataBits = new BitArray();
appendBytes(content, mode, dataBits, encoding);
// Step 3: Initialize QR code that can contain "dataBits".
int numInputBits = dataBits.getSize();
initQRCode(numInputBits, ecLevel, mode, qrCode);
// Hard part: need to know version to know how many bits length takes. But need to know how many
// bits it takes to know version. To pick version size assume length takes maximum bits
int bitsNeeded = headerBits.getSize()
+ mode.getCharacterCountBits(Version.getVersionForNumber(40))
+ dataBits.getSize();
Version version = chooseVersion(bitsNeeded, ecLevel);
// Step 4: Build another bit vector that contains header and data.
BitArray headerAndDataBits = new BitArray();
appendModeInfo(mode, headerAndDataBits);
headerAndDataBits.appendBitArray(headerBits);
// Find "length" of main segment and write it
int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();
appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits);
appendLengthInfo(numLetters, version, mode, headerAndDataBits);
// Put data together into the overall payload
headerAndDataBits.appendBitArray(dataBits);
// Step 5: Terminate the bits properly.
terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();
// Step 6: Interleave data bits with error correction code.
BitArray finalBits = new BitArray();
interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
qrCode.getNumRSBlocks(), finalBits);
// Terminate the bits properly.
terminateBits(numDataBytes, headerAndDataBits);
// Step 7: Choose the mask pattern and set to "qrCode".
ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
qrCode.setMaskPattern(chooseMaskPattern(finalBits, ecLevel, qrCode.getVersion(), matrix));
// Interleave data bits with error correction code.
BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
version.getTotalCodewords(),
numDataBytes,
ecBlocks.getNumBlocks());
// Step 8. Build the matrix and set it to "qrCode".
MatrixUtil.buildMatrix(finalBits, ecLevel, qrCode.getVersion(), qrCode.getMaskPattern(), matrix);
QRCode qrCode = new QRCode();
qrCode.setECLevel(ecLevel);
qrCode.setMode(mode);
qrCode.setVersion(version);
// Choose the mask pattern and set to "qrCode".
int dimension = version.getDimensionForVersion();
ByteMatrix matrix = new ByteMatrix(dimension, dimension);
int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
qrCode.setMaskPattern(maskPattern);
// Build the matrix and set it to "qrCode".
MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
qrCode.setMatrix(matrix);
// Step 9. Make sure we have a valid QR Code.
if (!qrCode.isValid()) {
throw new WriterException("Invalid QR code: " + qrCode.toString());
}
return qrCode;
}
/**
@ -206,7 +223,7 @@ public final class Encoder {
private static int chooseMaskPattern(BitArray bits,
ErrorCorrectionLevel ecLevel,
int version,
Version version,
ByteMatrix matrix) throws WriterException {
int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
@ -223,17 +240,7 @@ public final class Encoder {
return bestMaskPattern;
}
/**
* Initialize "qrCode" according to "numInputBits", "ecLevel", and "mode". On success,
* modify "qrCode".
*/
private static void initQRCode(int numInputBits,
ErrorCorrectionLevel ecLevel,
Mode mode,
QRCode qrCode) throws WriterException {
qrCode.setECLevel(ecLevel);
qrCode.setMode(mode);
private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException {
// In the following comments, we use numbers of Version 7-H.
for (int versionNum = 1; versionNum <= 40; versionNum++) {
Version version = Version.getVersionForNumber(versionNum);
@ -242,36 +249,14 @@ public final class Encoder {
// getNumECBytes = 130
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getTotalECCodewords();
// getNumRSBlocks = 5
int numRSBlocks = ecBlocks.getNumBlocks();
// getNumDataBytes = 196 - 130 = 66
int numDataBytes = numBytes - numEcBytes;
// We want to choose the smallest version which can contain data of "numInputBytes" + some
// extra bits for the header (mode info and length info). The header can be three bytes
// (precisely 4 + 16 bits) at most.
if (numDataBytes >= getTotalInputBytes(numInputBits, version, mode)) {
// Yay, we found the proper rs block info!
qrCode.setVersion(versionNum);
qrCode.setNumTotalBytes(numBytes);
qrCode.setNumDataBytes(numDataBytes);
qrCode.setNumRSBlocks(numRSBlocks);
// getNumECBytes = 196 - 66 = 130
qrCode.setNumECBytes(numEcBytes);
// matrix width = 21 + 6 * 4 = 45
qrCode.setMatrixWidth(version.getDimensionForVersion());
return;
int totalInputBytes = (numInputBits + 7) / 8;
if (numDataBytes >= totalInputBytes) {
return version;
}
}
throw new WriterException("Cannot find proper rs block info (input data too big?)");
}
private static int getTotalInputBytes(int numInputBits, Version version, Mode mode) {
int modeInfoBits = 4;
int charCountBits = mode.getCharacterCountBits(version);
int headerBits = modeInfoBits + charCountBits;
int totalBits = numInputBits + headerBits;
return (totalBits + 7) / 8;
throw new WriterException("Data too big");
}
/**
@ -365,11 +350,10 @@ public final class Encoder {
* Interleave "bits" with corresponding error correction bytes. On success, store the result in
* "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
*/
static void interleaveWithECBytes(BitArray bits,
int numTotalBytes,
int numDataBytes,
int numRSBlocks,
BitArray result) throws WriterException {
static BitArray interleaveWithECBytes(BitArray bits,
int numTotalBytes,
int numDataBytes,
int numRSBlocks) throws WriterException {
// "bits" must have "getNumDataBytes" bytes of data.
if (bits.getSizeInBytes() != numDataBytes) {
@ -406,6 +390,8 @@ public final class Encoder {
throw new WriterException("Data bytes does not match offset");
}
BitArray result = new BitArray();
// First, place data blocks.
for (int i = 0; i < maxNumDataBytes; ++i) {
for (BlockPair block : blocks) {
@ -428,6 +414,8 @@ public final class Encoder {
throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
result.getSizeInBytes() + " differ.");
}
return result;
}
static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {
@ -456,11 +444,10 @@ public final class Encoder {
/**
* Append length info. On success, store the result in "bits".
*/
static void appendLengthInfo(int numLetters, int version, Mode mode, BitArray bits)
throws WriterException {
int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));
if (numLetters > ((1 << numBits) - 1)) {
throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException {
int numBits = mode.getCharacterCountBits(version);
if (numLetters >= (1 << numBits)) {
throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1));
}
bits.appendBits(numLetters, numBits);
}

View file

@ -19,6 +19,7 @@ package com.google.zxing.qrcode.encoder;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Version;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
@ -40,14 +41,6 @@ final class MatrixUtil {
{1, 1, 1, 1, 1, 1, 1},
};
private static final int[][] HORIZONTAL_SEPARATION_PATTERN = {
{0, 0, 0, 0, 0, 0, 0, 0},
};
private static final int[][] VERTICAL_SEPARATION_PATTERN = {
{0}, {0}, {0}, {0}, {0}, {0}, {0},
};
private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
@ -138,7 +131,7 @@ final class MatrixUtil {
// success, store the result in "matrix" and return true.
static void buildMatrix(BitArray dataBits,
ErrorCorrectionLevel ecLevel,
int version,
Version version,
int maskPattern,
ByteMatrix matrix) throws WriterException {
clearMatrix(matrix);
@ -157,7 +150,7 @@ final class MatrixUtil {
// - Timing patterns
// - Dark dot at the left bottom corner
// - Position adjustment patterns, if need be
static void embedBasicPatterns(int version, ByteMatrix matrix) throws WriterException {
static void embedBasicPatterns(Version version, ByteMatrix matrix) throws WriterException {
// Let's get started with embedding big squares at corners.
embedPositionDetectionPatternsAndSeparators(matrix);
// Then, embed the dark dot at the left bottom corner.
@ -201,8 +194,8 @@ final class MatrixUtil {
// Embed version information if need be. On success, modify the matrix and return true.
// See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException {
if (version < 7) { // Version info is necessary if version >= 7.
static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix) throws WriterException {
if (version.getVersionNumber() < 7) { // Version info is necessary if version >= 7.
return; // Don't need version info.
}
BitArray versionInfoBits = new BitArray();
@ -349,9 +342,9 @@ final class MatrixUtil {
// Make bit vector of version information. On success, store the result in "bits" and return true.
// See 8.10 of JISX0510:2004 (p.45) for details.
static void makeVersionInfoBits(int version, BitArray bits) throws WriterException {
bits.appendBits(version, 6);
int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);
static void makeVersionInfoBits(Version version, BitArray bits) throws WriterException {
bits.appendBits(version.getVersionNumber(), 6);
int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY);
bits.appendBits(bchCode, 12);
if (bits.getSize() != 18) { // Just in case.
@ -364,29 +357,16 @@ final class MatrixUtil {
return value == -1;
}
// Check if "value" is valid.
private static boolean isValidValue(int value) {
return value == -1 || // Empty.
value == 0 || // Light (white).
value == 1; // Dark (black).
}
private static void embedTimingPatterns(ByteMatrix matrix) throws WriterException {
private static void embedTimingPatterns(ByteMatrix matrix) {
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
// separation patterns (size 1). Thus, 8 = 7 + 1.
for (int i = 8; i < matrix.getWidth() - 8; ++i) {
int bit = (i + 1) % 2;
// Horizontal line.
if (!isValidValue(matrix.get(i, 6))) {
throw new WriterException();
}
if (isEmpty(matrix.get(i, 6))) {
matrix.set(i, 6, bit);
}
// Vertical line.
if (!isValidValue(matrix.get(6, i))) {
throw new WriterException();
}
if (isEmpty(matrix.get(6, i))) {
matrix.set(6, i, bit);
}
@ -404,65 +384,39 @@ final class MatrixUtil {
private static void embedHorizontalSeparationPattern(int xStart,
int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (HORIZONTAL_SEPARATION_PATTERN[0].length != 8 || HORIZONTAL_SEPARATION_PATTERN.length != 1) {
throw new WriterException("Bad horizontal separation pattern");
}
for (int x = 0; x < 8; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart))) {
throw new WriterException();
}
matrix.set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
matrix.set(xStart + x, yStart, 0);
}
}
private static void embedVerticalSeparationPattern(int xStart,
int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (VERTICAL_SEPARATION_PATTERN[0].length != 1 || VERTICAL_SEPARATION_PATTERN.length != 7) {
throw new WriterException("Bad vertical separation pattern");
}
for (int y = 0; y < 7; ++y) {
if (!isEmpty(matrix.get(xStart, yStart + y))) {
throw new WriterException();
}
matrix.set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
matrix.set(xStart, yStart + y, 0);
}
}
// Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
// almost identical, since we cannot write a function that takes 2D arrays in different sizes in
// C/C++. We should live with the fact.
private static void embedPositionAdjustmentPattern(int xStart,
int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (POSITION_ADJUSTMENT_PATTERN[0].length != 5 || POSITION_ADJUSTMENT_PATTERN.length != 5) {
throw new WriterException("Bad position adjustment");
}
private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) {
for (int y = 0; y < 5; ++y) {
for (int x = 0; x < 5; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
throw new WriterException();
}
matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
}
}
}
private static void embedPositionDetectionPattern(int xStart,
int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (POSITION_DETECTION_PATTERN[0].length != 7 || POSITION_DETECTION_PATTERN.length != 7) {
throw new WriterException("Bad position detection pattern");
}
private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) {
for (int y = 0; y < 7; ++y) {
for (int x = 0; x < 7; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
throw new WriterException();
}
matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
}
}
@ -480,7 +434,7 @@ final class MatrixUtil {
embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
// Embed horizontal separation patterns around the squares.
int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].length;
int hspWidth = 8;
// Left top corner.
embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
// Right top corner.
@ -490,7 +444,7 @@ final class MatrixUtil {
embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
// Embed vertical separation patterns around the squares.
int vspSize = VERTICAL_SEPARATION_PATTERN.length;
int vspSize = 7;
// Left top corner.
embedVerticalSeparationPattern(vspSize, 0, matrix);
// Right top corner.
@ -501,12 +455,11 @@ final class MatrixUtil {
}
// Embed position adjustment patterns if need be.
private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix)
throws WriterException {
if (version < 2) { // The patterns appear if version >= 2
private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix) {
if (version.getVersionNumber() < 2) { // The patterns appear if version >= 2
return;
}
int index = version - 1;
int index = version.getVersionNumber() - 1;
int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
for (int i = 0; i < numCoordinates; ++i) {

View file

@ -18,6 +18,7 @@ package com.google.zxing.qrcode.encoder;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
@ -29,115 +30,34 @@ public final class QRCode {
private Mode mode;
private ErrorCorrectionLevel ecLevel;
private int version;
private int matrixWidth;
private Version version;
private int maskPattern;
private int numTotalBytes;
private int numDataBytes;
private int numECBytes;
private int numRSBlocks;
private ByteMatrix matrix;
public QRCode() {
mode = null;
ecLevel = null;
version = -1;
matrixWidth = -1;
maskPattern = -1;
numTotalBytes = -1;
numDataBytes = -1;
numECBytes = -1;
numRSBlocks = -1;
matrix = null;
}
// Mode of the QR Code.
public Mode getMode() {
return mode;
}
// Error correction level of the QR Code.
public ErrorCorrectionLevel getECLevel() {
return ecLevel;
}
// Version of the QR Code. The bigger size, the bigger version.
public int getVersion() {
public Version getVersion() {
return version;
}
// ByteMatrix width of the QR Code.
public int getMatrixWidth() {
return matrixWidth;
}
// Mask pattern of the QR Code.
public int getMaskPattern() {
return maskPattern;
}
// Number of total bytes in the QR Code.
public int getNumTotalBytes() {
return numTotalBytes;
}
// Number of data bytes in the QR Code.
public int getNumDataBytes() {
return numDataBytes;
}
// Number of error correction bytes in the QR Code.
public int getNumECBytes() {
return numECBytes;
}
// Number of Reedsolomon blocks in the QR Code.
public int getNumRSBlocks() {
return numRSBlocks;
}
// ByteMatrix data of the QR Code.
public ByteMatrix getMatrix() {
return matrix;
}
// Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
// call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
public int at(int x, int y) {
// The value must be zero or one.
int value = matrix.get(x, y);
if (!(value == 0 || value == 1)) {
throw new IllegalStateException("Bad value");
}
return value;
}
// Checks all the member variables are set properly. Returns true on success. Otherwise, returns
// false.
public boolean isValid() {
return
// First check if all version are not uninitialized.
mode != null &&
ecLevel != null &&
version != -1 &&
matrixWidth != -1 &&
maskPattern != -1 &&
numTotalBytes != -1 &&
numDataBytes != -1 &&
numECBytes != -1 &&
numRSBlocks != -1 &&
// Then check them in other ways..
isValidMaskPattern(maskPattern) &&
numTotalBytes == numDataBytes + numECBytes &&
// ByteMatrix stuff.
matrix != null &&
matrixWidth == matrix.getWidth() &&
// See 7.3.1 of JISX0510:2004 (p.5).
matrix.getWidth() == matrix.getHeight(); // Must be square.
}
// Return debug String.
@Override
public String toString() {
StringBuilder result = new StringBuilder(200);
@ -148,18 +68,8 @@ public final class QRCode {
result.append(ecLevel);
result.append("\n version: ");
result.append(version);
result.append("\n matrixWidth: ");
result.append(matrixWidth);
result.append("\n maskPattern: ");
result.append(maskPattern);
result.append("\n numTotalBytes: ");
result.append(numTotalBytes);
result.append("\n numDataBytes: ");
result.append(numDataBytes);
result.append("\n numECBytes: ");
result.append(numECBytes);
result.append("\n numRSBlocks: ");
result.append(numRSBlocks);
if (matrix == null) {
result.append("\n matrix: null\n");
} else {
@ -178,35 +88,14 @@ public final class QRCode {
ecLevel = value;
}
public void setVersion(int value) {
version = value;
}
public void setMatrixWidth(int value) {
matrixWidth = value;
public void setVersion(Version version) {
this.version = version;
}
public void setMaskPattern(int value) {
maskPattern = value;
}
public void setNumTotalBytes(int value) {
numTotalBytes = value;
}
public void setNumDataBytes(int value) {
numDataBytes = value;
}
public void setNumECBytes(int value) {
numECBytes = value;
}
public void setNumRSBlocks(int value) {
numRSBlocks = value;
}
// This takes ownership of the 2D array.
public void setMatrix(ByteMatrix value) {
matrix = value;
}

View file

@ -16,14 +16,18 @@
package com.google.zxing.qrcode.encoder;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
import org.junit.Assert;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.util.EnumMap;
import java.util.Map;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
@ -90,20 +94,13 @@ public final class EncoderTestCase extends Assert {
@Test
public void testEncode() throws WriterException {
QRCode qrCode = new QRCode();
Encoder.encode("ABCDEF", ErrorCorrectionLevel.H, qrCode);
// The following is a valid QR Code that can be read by cell phones.
QRCode qrCode = Encoder.encode("ABCDEF", ErrorCorrectionLevel.H);
String expected =
"<<\n" +
" mode: ALPHANUMERIC\n" +
" ecLevel: H\n" +
" version: 1\n" +
" matrixWidth: 21\n" +
" maskPattern: 0\n" +
" numTotalBytes: 26\n" +
" numDataBytes: 9\n" +
" numECBytes: 17\n" +
" numRSBlocks: 1\n" +
" matrix:\n" +
" 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n" +
" 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n" +
@ -130,6 +127,43 @@ public final class EncoderTestCase extends Assert {
assertEquals(expected, qrCode.toString());
}
@Test
public void testSimpleUTF8ECI() throws WriterException {
Map<EncodeHintType,Object> hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, "UTF8");
QRCode qrCode = Encoder.encode("hello", ErrorCorrectionLevel.H, hints);
String expected =
"<<\n" +
" mode: BYTE\n" +
" ecLevel: H\n" +
" version: 1\n" +
" maskPattern: 3\n" +
" matrix:\n" +
" 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" +
" 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n" +
" 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n" +
" 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n" +
" 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n" +
" 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n" +
" 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" +
" 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n" +
" 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n" +
" 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n" +
" 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n" +
" 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n" +
" 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n" +
" 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n" +
" 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n" +
" 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n" +
" 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n" +
" 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n" +
" 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n" +
" 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n" +
" 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n" +
">>\n";
assertEquals(expected, qrCode.toString());
}
@Test
public void testAppendModeInfo() {
BitArray bits = new BitArray();
@ -142,7 +176,7 @@ public final class EncoderTestCase extends Assert {
{
BitArray bits = new BitArray();
Encoder.appendLengthInfo(1, // 1 letter (1/1).
1, // version 1.
Version.getVersionForNumber(1),
Mode.NUMERIC,
bits);
assertEquals(" ........ .X", bits.toString()); // 10 bits.
@ -150,7 +184,7 @@ public final class EncoderTestCase extends Assert {
{
BitArray bits = new BitArray();
Encoder.appendLengthInfo(2, // 2 letters (2/1).
10, // version 10.
Version.getVersionForNumber(10),
Mode.ALPHANUMERIC,
bits);
assertEquals(" ........ .X.", bits.toString()); // 11 bits.
@ -158,7 +192,7 @@ public final class EncoderTestCase extends Assert {
{
BitArray bits = new BitArray();
Encoder.appendLengthInfo(255, // 255 letter (255/1).
27, // version 27.
Version.getVersionForNumber(27),
Mode.BYTE,
bits);
assertEquals(" ........ XXXXXXXX", bits.toString()); // 16 bits.
@ -166,7 +200,7 @@ public final class EncoderTestCase extends Assert {
{
BitArray bits = new BitArray();
Encoder.appendLengthInfo(512, // 512 letters (1024/2).
40, // version 40.
Version.getVersionForNumber(40),
Mode.KANJI,
bits);
assertEquals(" ..X..... ....", bits.toString()); // 12 bits.
@ -301,8 +335,7 @@ public final class EncoderTestCase extends Assert {
for (byte dataByte: dataBytes) {
in.appendBits(dataByte, 8);
}
BitArray out = new BitArray();
Encoder.interleaveWithECBytes(in, 26, 9, 1, out);
BitArray out = Encoder.interleaveWithECBytes(in, 26, 9, 1);
byte[] expected = {
// Data bytes.
32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236,
@ -332,8 +365,8 @@ public final class EncoderTestCase extends Assert {
for (byte dataByte: dataBytes) {
in.appendBits(dataByte, 8);
}
BitArray out = new BitArray();
Encoder.interleaveWithECBytes(in, 134, 62, 4, out);
BitArray out = Encoder.interleaveWithECBytes(in, 134, 62, 4);
byte[] expected = {
// Data bytes.
67, (byte)230, 54, 55, 70, (byte)247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39,
@ -532,8 +565,7 @@ public final class EncoderTestCase extends Assert {
for (int x = 0; x < 3518; x++) {
builder.append('0');
}
QRCode qrCode = new QRCode();
Encoder.encode(builder.toString(), ErrorCorrectionLevel.L, qrCode);
Encoder.encode(builder.toString(), ErrorCorrectionLevel.L);
}
private static String shiftJISString(byte[] bytes) throws WriterException {

View file

@ -19,6 +19,7 @@ package com.google.zxing.qrcode.encoder;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Version;
import org.junit.Assert;
import org.junit.Test;
@ -59,7 +60,7 @@ public final class MatrixUtilTestCase extends Assert {
// Version 1.
ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.clearMatrix(matrix);
MatrixUtil.embedBasicPatterns(1, matrix);
MatrixUtil.embedBasicPatterns(Version.getVersionForNumber(1), matrix);
String expected =
" 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" +
" 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" +
@ -91,7 +92,7 @@ public final class MatrixUtilTestCase extends Assert {
// bottom corner.
ByteMatrix matrix = new ByteMatrix(25, 25);
MatrixUtil.clearMatrix(matrix);
MatrixUtil.embedBasicPatterns(2, matrix);
MatrixUtil.embedBasicPatterns(Version.getVersionForNumber(2), matrix);
String expected =
" 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" +
" 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" +
@ -159,7 +160,7 @@ public final class MatrixUtilTestCase extends Assert {
// since 45x45 matrix is too big to depict.
ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.clearMatrix(matrix);
MatrixUtil.maybeEmbedVersionInfo(7, matrix);
MatrixUtil.maybeEmbedVersionInfo(Version.getVersionForNumber(7), matrix);
String expected =
" 0 0 1 \n" +
" 0 1 0 \n" +
@ -191,7 +192,7 @@ public final class MatrixUtilTestCase extends Assert {
BitArray bits = new BitArray();
ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.clearMatrix(matrix);
MatrixUtil.embedBasicPatterns(1, matrix);
MatrixUtil.embedBasicPatterns(Version.getVersionForNumber(1), matrix);
MatrixUtil.embedDataBits(bits, -1, matrix);
String expected =
" 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" +
@ -231,7 +232,7 @@ public final class MatrixUtilTestCase extends Assert {
ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.buildMatrix(bits,
ErrorCorrectionLevel.H,
1, // Version 1
Version.getVersionForNumber(1), // Version 1
3, // Mask pattern 3
matrix);
String expected =
@ -294,7 +295,7 @@ public final class MatrixUtilTestCase extends Assert {
public void testMakeVersionInfoBits() throws WriterException {
// From Appendix D in JISX0510:2004 (p 68)
BitArray bits = new BitArray();
MatrixUtil.makeVersionInfoBits(7, bits);
MatrixUtil.makeVersionInfoBits(Version.getVersionForNumber(7), bits);
assertEquals(" ...XXXXX ..X..X.X ..", bits.toString());
}

View file

@ -18,6 +18,7 @@ package com.google.zxing.qrcode.encoder;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
import org.junit.Assert;
import org.junit.Test;
@ -30,33 +31,18 @@ public final class QRCodeTestCase extends Assert {
@Test
public void test() {
QRCode qrCode = new QRCode();
// Initially the QR Code should be invalid.
assertFalse(qrCode.isValid());
// First, test simple setters and getters.
// We use numbers of version 7-H.
qrCode.setMode(Mode.BYTE);
qrCode.setECLevel(ErrorCorrectionLevel.H);
qrCode.setVersion(7);
qrCode.setMatrixWidth(45);
qrCode.setVersion(Version.getVersionForNumber(7));
qrCode.setMaskPattern(3);
qrCode.setNumTotalBytes(196);
qrCode.setNumDataBytes(66);
qrCode.setNumECBytes(130);
qrCode.setNumRSBlocks(5);
assertSame(Mode.BYTE, qrCode.getMode());
assertSame(ErrorCorrectionLevel.H, qrCode.getECLevel());
assertEquals(7, qrCode.getVersion());
assertEquals(45, qrCode.getMatrixWidth());
assertEquals(7, qrCode.getVersion().getVersionNumber());
assertEquals(3, qrCode.getMaskPattern());
assertEquals(196, qrCode.getNumTotalBytes());
assertEquals(66, qrCode.getNumDataBytes());
assertEquals(130, qrCode.getNumECBytes());
assertEquals(5, qrCode.getNumRSBlocks());
// It still should be invalid.
assertFalse(qrCode.isValid());
// Prepare the matrix.
ByteMatrix matrix = new ByteMatrix(45, 45);
@ -70,16 +56,6 @@ public final class QRCodeTestCase extends Assert {
// Set the matrix.
qrCode.setMatrix(matrix);
assertSame(matrix, qrCode.getMatrix());
// Finally, it should be valid.
assertTrue(qrCode.isValid());
// Make sure "at()" returns the same value.
for (int y = 0; y < 45; ++y) {
for (int x = 0; x < 45; ++x) {
assertEquals((y + x) % 2, qrCode.at(x, y));
}
}
}
@Test
@ -89,13 +65,8 @@ public final class QRCodeTestCase extends Assert {
"<<\n" +
" mode: null\n" +
" ecLevel: null\n" +
" version: -1\n" +
" matrixWidth: -1\n" +
" version: null\n" +
" maskPattern: -1\n" +
" numTotalBytes: -1\n" +
" numDataBytes: -1\n" +
" numECBytes: -1\n" +
" numRSBlocks: -1\n" +
" matrix: null\n" +
">>\n";
assertEquals(expected, qrCode.toString());
@ -106,13 +77,8 @@ public final class QRCodeTestCase extends Assert {
QRCode qrCode = new QRCode();
qrCode.setMode(Mode.BYTE);
qrCode.setECLevel(ErrorCorrectionLevel.H);
qrCode.setVersion(1);
qrCode.setMatrixWidth(21);
qrCode.setVersion(Version.getVersionForNumber(1));
qrCode.setMaskPattern(3);
qrCode.setNumTotalBytes(26);
qrCode.setNumDataBytes(9);
qrCode.setNumECBytes(17);
qrCode.setNumRSBlocks(1);
ByteMatrix matrix = new ByteMatrix(21, 21);
for (int y = 0; y < 21; ++y) {
for (int x = 0; x < 21; ++x) {
@ -120,17 +86,11 @@ public final class QRCodeTestCase extends Assert {
}
}
qrCode.setMatrix(matrix);
assertTrue(qrCode.isValid());
String expected = "<<\n" +
" mode: BYTE\n" +
" ecLevel: H\n" +
" version: 1\n" +
" matrixWidth: 21\n" +
" maskPattern: 3\n" +
" numTotalBytes: 26\n" +
" numDataBytes: 9\n" +
" numECBytes: 17\n" +
" numRSBlocks: 1\n" +
" matrix:\n" +
" 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0\n" +
" 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1\n" +