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(); QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
Encoder.encode(contents, errorCorrectionLevel, hints, code);
return renderResult(code, width, height); 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. // 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. // Basically it applies four rules and summate all penalties.
private static int calculateMaskPenalty(ByteMatrix matrix) { private static int calculateMaskPenalty(ByteMatrix matrix) {
int penalty = 0; return MaskUtil.applyMaskPenaltyRule1(matrix)
penalty += MaskUtil.applyMaskPenaltyRule1(matrix); + MaskUtil.applyMaskPenaltyRule2(matrix)
penalty += MaskUtil.applyMaskPenaltyRule2(matrix); + MaskUtil.applyMaskPenaltyRule3(matrix)
penalty += MaskUtil.applyMaskPenaltyRule3(matrix); + MaskUtil.applyMaskPenaltyRule4(matrix);
penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
return penalty;
} }
/** /**
@ -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() * 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. * 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) public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException {
throws WriterException { return encode(content, ecLevel, null);
encode(content, ecLevel, null, qrCode);
} }
public static void encode(String content, public static QRCode encode(String content,
ErrorCorrectionLevel ecLevel, ErrorCorrectionLevel ecLevel,
Map<EncodeHintType,?> hints, Map<EncodeHintType,?> hints) throws WriterException {
QRCode qrCode) 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); String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
if (encoding == null) { if (encoding == null) {
encoding = DEFAULT_BYTE_MODE_ENCODING; 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); 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)) { if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding); CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
if (eci != null) { 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); appendBytes(content, mode, dataBits, encoding);
// Step 3: Initialize QR code that can contain "dataBits". // Hard part: need to know version to know how many bits length takes. But need to know how many
int numInputBits = dataBits.getSize(); // bits it takes to know version. To pick version size assume length takes maximum bits
initQRCode(numInputBits, ecLevel, mode, qrCode); 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(); BitArray headerAndDataBits = new BitArray();
headerAndDataBits.appendBitArray(headerBits);
appendModeInfo(mode, headerAndDataBits); // Find "length" of main segment and write it
int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); 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); headerAndDataBits.appendBitArray(dataBits);
// Step 5: Terminate the bits properly. Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
terminateBits(qrCode.getNumDataBytes(), headerAndDataBits); int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();
// Step 6: Interleave data bits with error correction code. // Terminate the bits properly.
BitArray finalBits = new BitArray(); terminateBits(numDataBytes, headerAndDataBits);
interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
qrCode.getNumRSBlocks(), finalBits);
// Step 7: Choose the mask pattern and set to "qrCode". // Interleave data bits with error correction code.
ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth()); BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
qrCode.setMaskPattern(chooseMaskPattern(finalBits, ecLevel, qrCode.getVersion(), matrix)); version.getTotalCodewords(),
numDataBytes,
ecBlocks.getNumBlocks());
// Step 8. Build the matrix and set it to "qrCode". QRCode qrCode = new QRCode();
MatrixUtil.buildMatrix(finalBits, ecLevel, qrCode.getVersion(), qrCode.getMaskPattern(), matrix);
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); qrCode.setMatrix(matrix);
// Step 9. Make sure we have a valid QR Code.
if (!qrCode.isValid()) { return qrCode;
throw new WriterException("Invalid QR code: " + qrCode.toString());
}
} }
/** /**
@ -206,7 +223,7 @@ public final class Encoder {
private static int chooseMaskPattern(BitArray bits, private static int chooseMaskPattern(BitArray bits,
ErrorCorrectionLevel ecLevel, ErrorCorrectionLevel ecLevel,
int version, Version version,
ByteMatrix matrix) throws WriterException { ByteMatrix matrix) throws WriterException {
int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
@ -223,17 +240,7 @@ public final class Encoder {
return bestMaskPattern; return bestMaskPattern;
} }
/** private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException {
* 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);
// In the following comments, we use numbers of Version 7-H. // In the following comments, we use numbers of Version 7-H.
for (int versionNum = 1; versionNum <= 40; versionNum++) { for (int versionNum = 1; versionNum <= 40; versionNum++) {
Version version = Version.getVersionForNumber(versionNum); Version version = Version.getVersionForNumber(versionNum);
@ -242,36 +249,14 @@ public final class Encoder {
// getNumECBytes = 130 // getNumECBytes = 130
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getTotalECCodewords(); int numEcBytes = ecBlocks.getTotalECCodewords();
// getNumRSBlocks = 5
int numRSBlocks = ecBlocks.getNumBlocks();
// getNumDataBytes = 196 - 130 = 66 // getNumDataBytes = 196 - 130 = 66
int numDataBytes = numBytes - numEcBytes; int numDataBytes = numBytes - numEcBytes;
// We want to choose the smallest version which can contain data of "numInputBytes" + some int totalInputBytes = (numInputBits + 7) / 8;
// extra bits for the header (mode info and length info). The header can be three bytes if (numDataBytes >= totalInputBytes) {
// (precisely 4 + 16 bits) at most. return version;
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;
} }
} }
throw new WriterException("Cannot find proper rs block info (input data too big?)"); throw new WriterException("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;
} }
/** /**
@ -365,11 +350,10 @@ public final class Encoder {
* Interleave "bits" with corresponding error correction bytes. On success, store the result in * 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. * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
*/ */
static void interleaveWithECBytes(BitArray bits, static BitArray interleaveWithECBytes(BitArray bits,
int numTotalBytes, int numTotalBytes,
int numDataBytes, int numDataBytes,
int numRSBlocks, int numRSBlocks) throws WriterException {
BitArray result) throws WriterException {
// "bits" must have "getNumDataBytes" bytes of data. // "bits" must have "getNumDataBytes" bytes of data.
if (bits.getSizeInBytes() != numDataBytes) { if (bits.getSizeInBytes() != numDataBytes) {
@ -406,6 +390,8 @@ public final class Encoder {
throw new WriterException("Data bytes does not match offset"); throw new WriterException("Data bytes does not match offset");
} }
BitArray result = new BitArray();
// First, place data blocks. // First, place data blocks.
for (int i = 0; i < maxNumDataBytes; ++i) { for (int i = 0; i < maxNumDataBytes; ++i) {
for (BlockPair block : blocks) { for (BlockPair block : blocks) {
@ -428,6 +414,8 @@ public final class Encoder {
throw new WriterException("Interleaving error: " + numTotalBytes + " and " + throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
result.getSizeInBytes() + " differ."); result.getSizeInBytes() + " differ.");
} }
return result;
} }
static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) { 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". * Append length info. On success, store the result in "bits".
*/ */
static void appendLengthInfo(int numLetters, int version, Mode mode, BitArray bits) static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException {
throws WriterException { int numBits = mode.getCharacterCountBits(version);
int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version)); if (numLetters >= (1 << numBits)) {
if (numLetters > ((1 << numBits) - 1)) { throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1));
throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
} }
bits.appendBits(numLetters, numBits); 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.WriterException;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Version;
/** /**
* @author satorux@google.com (Satoru Takabayashi) - creator * @author satorux@google.com (Satoru Takabayashi) - creator
@ -40,14 +41,6 @@ final class MatrixUtil {
{1, 1, 1, 1, 1, 1, 1}, {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 = { private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1},
{1, 0, 0, 0, 1}, {1, 0, 0, 0, 1},
@ -138,7 +131,7 @@ final class MatrixUtil {
// success, store the result in "matrix" and return true. // success, store the result in "matrix" and return true.
static void buildMatrix(BitArray dataBits, static void buildMatrix(BitArray dataBits,
ErrorCorrectionLevel ecLevel, ErrorCorrectionLevel ecLevel,
int version, Version version,
int maskPattern, int maskPattern,
ByteMatrix matrix) throws WriterException { ByteMatrix matrix) throws WriterException {
clearMatrix(matrix); clearMatrix(matrix);
@ -157,7 +150,7 @@ final class MatrixUtil {
// - Timing patterns // - Timing patterns
// - Dark dot at the left bottom corner // - Dark dot at the left bottom corner
// - Position adjustment patterns, if need be // - 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. // Let's get started with embedding big squares at corners.
embedPositionDetectionPatternsAndSeparators(matrix); embedPositionDetectionPatternsAndSeparators(matrix);
// Then, embed the dark dot at the left bottom corner. // 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. // 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. // See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException { static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix) throws WriterException {
if (version < 7) { // Version info is necessary if version >= 7. if (version.getVersionNumber() < 7) { // Version info is necessary if version >= 7.
return; // Don't need version info. return; // Don't need version info.
} }
BitArray versionInfoBits = new BitArray(); 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. // 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. // See 8.10 of JISX0510:2004 (p.45) for details.
static void makeVersionInfoBits(int version, BitArray bits) throws WriterException { static void makeVersionInfoBits(Version version, BitArray bits) throws WriterException {
bits.appendBits(version, 6); bits.appendBits(version.getVersionNumber(), 6);
int bchCode = calculateBCHCode(version, VERSION_INFO_POLY); int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY);
bits.appendBits(bchCode, 12); bits.appendBits(bchCode, 12);
if (bits.getSize() != 18) { // Just in case. if (bits.getSize() != 18) { // Just in case.
@ -364,29 +357,16 @@ final class MatrixUtil {
return value == -1; return value == -1;
} }
// Check if "value" is valid. private static void embedTimingPatterns(ByteMatrix matrix) {
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 {
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
// separation patterns (size 1). Thus, 8 = 7 + 1. // separation patterns (size 1). Thus, 8 = 7 + 1.
for (int i = 8; i < matrix.getWidth() - 8; ++i) { for (int i = 8; i < matrix.getWidth() - 8; ++i) {
int bit = (i + 1) % 2; int bit = (i + 1) % 2;
// Horizontal line. // Horizontal line.
if (!isValidValue(matrix.get(i, 6))) {
throw new WriterException();
}
if (isEmpty(matrix.get(i, 6))) { if (isEmpty(matrix.get(i, 6))) {
matrix.set(i, 6, bit); matrix.set(i, 6, bit);
} }
// Vertical line. // Vertical line.
if (!isValidValue(matrix.get(6, i))) {
throw new WriterException();
}
if (isEmpty(matrix.get(6, i))) { if (isEmpty(matrix.get(6, i))) {
matrix.set(6, i, bit); matrix.set(6, i, bit);
} }
@ -404,65 +384,39 @@ final class MatrixUtil {
private static void embedHorizontalSeparationPattern(int xStart, private static void embedHorizontalSeparationPattern(int xStart,
int yStart, int yStart,
ByteMatrix matrix) throws WriterException { 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) { for (int x = 0; x < 8; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart))) { if (!isEmpty(matrix.get(xStart + x, yStart))) {
throw new WriterException(); 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, private static void embedVerticalSeparationPattern(int xStart,
int yStart, int yStart,
ByteMatrix matrix) throws WriterException { 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) { for (int y = 0; y < 7; ++y) {
if (!isEmpty(matrix.get(xStart, yStart + y))) { if (!isEmpty(matrix.get(xStart, yStart + y))) {
throw new WriterException(); 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 // 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 // almost identical, since we cannot write a function that takes 2D arrays in different sizes in
// C/C++. We should live with the fact. // C/C++. We should live with the fact.
private static void embedPositionAdjustmentPattern(int xStart, private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) {
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");
}
for (int y = 0; y < 5; ++y) { for (int y = 0; y < 5; ++y) {
for (int x = 0; x < 5; ++x) { 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]); matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
} }
} }
} }
private static void embedPositionDetectionPattern(int xStart, private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) {
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");
}
for (int y = 0; y < 7; ++y) { for (int y = 0; y < 7; ++y) {
for (int x = 0; x < 7; ++x) { 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]); matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
} }
} }
@ -480,7 +434,7 @@ final class MatrixUtil {
embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix); embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
// Embed horizontal separation patterns around the squares. // Embed horizontal separation patterns around the squares.
int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].length; int hspWidth = 8;
// Left top corner. // Left top corner.
embedHorizontalSeparationPattern(0, hspWidth - 1, matrix); embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
// Right top corner. // Right top corner.
@ -490,7 +444,7 @@ final class MatrixUtil {
embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix); embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
// Embed vertical separation patterns around the squares. // Embed vertical separation patterns around the squares.
int vspSize = VERTICAL_SEPARATION_PATTERN.length; int vspSize = 7;
// Left top corner. // Left top corner.
embedVerticalSeparationPattern(vspSize, 0, matrix); embedVerticalSeparationPattern(vspSize, 0, matrix);
// Right top corner. // Right top corner.
@ -501,12 +455,11 @@ final class MatrixUtil {
} }
// Embed position adjustment patterns if need be. // Embed position adjustment patterns if need be.
private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix) private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix) {
throws WriterException { if (version.getVersionNumber() < 2) { // The patterns appear if version >= 2
if (version < 2) { // The patterns appear if version >= 2
return; return;
} }
int index = version - 1; int index = version.getVersionNumber() - 1;
int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index]; int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length; int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
for (int i = 0; i < numCoordinates; ++i) { 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.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode; import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
/** /**
* @author satorux@google.com (Satoru Takabayashi) - creator * @author satorux@google.com (Satoru Takabayashi) - creator
@ -29,115 +30,34 @@ public final class QRCode {
private Mode mode; private Mode mode;
private ErrorCorrectionLevel ecLevel; private ErrorCorrectionLevel ecLevel;
private int version; private Version version;
private int matrixWidth;
private int maskPattern; private int maskPattern;
private int numTotalBytes;
private int numDataBytes;
private int numECBytes;
private int numRSBlocks;
private ByteMatrix matrix; private ByteMatrix matrix;
public QRCode() { public QRCode() {
mode = null;
ecLevel = null;
version = -1;
matrixWidth = -1;
maskPattern = -1; maskPattern = -1;
numTotalBytes = -1;
numDataBytes = -1;
numECBytes = -1;
numRSBlocks = -1;
matrix = null;
} }
// Mode of the QR Code.
public Mode getMode() { public Mode getMode() {
return mode; return mode;
} }
// Error correction level of the QR Code.
public ErrorCorrectionLevel getECLevel() { public ErrorCorrectionLevel getECLevel() {
return ecLevel; return ecLevel;
} }
// Version of the QR Code. The bigger size, the bigger version. public Version getVersion() {
public int getVersion() {
return version; return version;
} }
// ByteMatrix width of the QR Code.
public int getMatrixWidth() {
return matrixWidth;
}
// Mask pattern of the QR Code.
public int getMaskPattern() { public int getMaskPattern() {
return maskPattern; 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() { public ByteMatrix getMatrix() {
return matrix; 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 @Override
public String toString() { public String toString() {
StringBuilder result = new StringBuilder(200); StringBuilder result = new StringBuilder(200);
@ -148,18 +68,8 @@ public final class QRCode {
result.append(ecLevel); result.append(ecLevel);
result.append("\n version: "); result.append("\n version: ");
result.append(version); result.append(version);
result.append("\n matrixWidth: ");
result.append(matrixWidth);
result.append("\n maskPattern: "); result.append("\n maskPattern: ");
result.append(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) { if (matrix == null) {
result.append("\n matrix: null\n"); result.append("\n matrix: null\n");
} else { } else {
@ -178,35 +88,14 @@ public final class QRCode {
ecLevel = value; ecLevel = value;
} }
public void setVersion(int value) { public void setVersion(Version version) {
version = value; this.version = version;
}
public void setMatrixWidth(int value) {
matrixWidth = value;
} }
public void setMaskPattern(int value) { public void setMaskPattern(int value) {
maskPattern = 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) { public void setMatrix(ByteMatrix value) {
matrix = value; matrix = value;
} }

View file

@ -16,14 +16,18 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode; import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.EnumMap;
import java.util.Map;
/** /**
* @author satorux@google.com (Satoru Takabayashi) - creator * @author satorux@google.com (Satoru Takabayashi) - creator
@ -90,20 +94,13 @@ public final class EncoderTestCase extends Assert {
@Test @Test
public void testEncode() throws WriterException { public void testEncode() throws WriterException {
QRCode qrCode = new QRCode(); QRCode qrCode = Encoder.encode("ABCDEF", ErrorCorrectionLevel.H);
Encoder.encode("ABCDEF", ErrorCorrectionLevel.H, qrCode);
// The following is a valid QR Code that can be read by cell phones.
String expected = String expected =
"<<\n" + "<<\n" +
" mode: ALPHANUMERIC\n" + " mode: ALPHANUMERIC\n" +
" ecLevel: H\n" + " ecLevel: H\n" +
" version: 1\n" + " version: 1\n" +
" matrixWidth: 21\n" +
" maskPattern: 0\n" + " maskPattern: 0\n" +
" numTotalBytes: 26\n" +
" numDataBytes: 9\n" +
" numECBytes: 17\n" +
" numRSBlocks: 1\n" +
" matrix:\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 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" + " 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()); 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 @Test
public void testAppendModeInfo() { public void testAppendModeInfo() {
BitArray bits = new BitArray(); BitArray bits = new BitArray();
@ -142,7 +176,7 @@ public final class EncoderTestCase extends Assert {
{ {
BitArray bits = new BitArray(); BitArray bits = new BitArray();
Encoder.appendLengthInfo(1, // 1 letter (1/1). Encoder.appendLengthInfo(1, // 1 letter (1/1).
1, // version 1. Version.getVersionForNumber(1),
Mode.NUMERIC, Mode.NUMERIC,
bits); bits);
assertEquals(" ........ .X", bits.toString()); // 10 bits. assertEquals(" ........ .X", bits.toString()); // 10 bits.
@ -150,7 +184,7 @@ public final class EncoderTestCase extends Assert {
{ {
BitArray bits = new BitArray(); BitArray bits = new BitArray();
Encoder.appendLengthInfo(2, // 2 letters (2/1). Encoder.appendLengthInfo(2, // 2 letters (2/1).
10, // version 10. Version.getVersionForNumber(10),
Mode.ALPHANUMERIC, Mode.ALPHANUMERIC,
bits); bits);
assertEquals(" ........ .X.", bits.toString()); // 11 bits. assertEquals(" ........ .X.", bits.toString()); // 11 bits.
@ -158,7 +192,7 @@ public final class EncoderTestCase extends Assert {
{ {
BitArray bits = new BitArray(); BitArray bits = new BitArray();
Encoder.appendLengthInfo(255, // 255 letter (255/1). Encoder.appendLengthInfo(255, // 255 letter (255/1).
27, // version 27. Version.getVersionForNumber(27),
Mode.BYTE, Mode.BYTE,
bits); bits);
assertEquals(" ........ XXXXXXXX", bits.toString()); // 16 bits. assertEquals(" ........ XXXXXXXX", bits.toString()); // 16 bits.
@ -166,7 +200,7 @@ public final class EncoderTestCase extends Assert {
{ {
BitArray bits = new BitArray(); BitArray bits = new BitArray();
Encoder.appendLengthInfo(512, // 512 letters (1024/2). Encoder.appendLengthInfo(512, // 512 letters (1024/2).
40, // version 40. Version.getVersionForNumber(40),
Mode.KANJI, Mode.KANJI,
bits); bits);
assertEquals(" ..X..... ....", bits.toString()); // 12 bits. assertEquals(" ..X..... ....", bits.toString()); // 12 bits.
@ -301,8 +335,7 @@ public final class EncoderTestCase extends Assert {
for (byte dataByte: dataBytes) { for (byte dataByte: dataBytes) {
in.appendBits(dataByte, 8); in.appendBits(dataByte, 8);
} }
BitArray out = new BitArray(); BitArray out = Encoder.interleaveWithECBytes(in, 26, 9, 1);
Encoder.interleaveWithECBytes(in, 26, 9, 1, out);
byte[] expected = { byte[] expected = {
// Data bytes. // Data bytes.
32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236, 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) { for (byte dataByte: dataBytes) {
in.appendBits(dataByte, 8); 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 = { byte[] expected = {
// Data bytes. // Data bytes.
67, (byte)230, 54, 55, 70, (byte)247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39, 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++) { for (int x = 0; x < 3518; x++) {
builder.append('0'); builder.append('0');
} }
QRCode qrCode = new QRCode(); Encoder.encode(builder.toString(), ErrorCorrectionLevel.L);
Encoder.encode(builder.toString(), ErrorCorrectionLevel.L, qrCode);
} }
private static String shiftJISString(byte[] bytes) throws WriterException { 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.WriterException;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Version;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -59,7 +60,7 @@ public final class MatrixUtilTestCase extends Assert {
// Version 1. // Version 1.
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.clearMatrix(matrix); MatrixUtil.clearMatrix(matrix);
MatrixUtil.embedBasicPatterns(1, matrix); MatrixUtil.embedBasicPatterns(Version.getVersionForNumber(1), matrix);
String expected = String expected =
" 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + " 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" + " 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. // bottom corner.
ByteMatrix matrix = new ByteMatrix(25, 25); ByteMatrix matrix = new ByteMatrix(25, 25);
MatrixUtil.clearMatrix(matrix); MatrixUtil.clearMatrix(matrix);
MatrixUtil.embedBasicPatterns(2, matrix); MatrixUtil.embedBasicPatterns(Version.getVersionForNumber(2), matrix);
String expected = String expected =
" 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + " 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" + " 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. // since 45x45 matrix is too big to depict.
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.clearMatrix(matrix); MatrixUtil.clearMatrix(matrix);
MatrixUtil.maybeEmbedVersionInfo(7, matrix); MatrixUtil.maybeEmbedVersionInfo(Version.getVersionForNumber(7), matrix);
String expected = String expected =
" 0 0 1 \n" + " 0 0 1 \n" +
" 0 1 0 \n" + " 0 1 0 \n" +
@ -191,7 +192,7 @@ public final class MatrixUtilTestCase extends Assert {
BitArray bits = new BitArray(); BitArray bits = new BitArray();
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.clearMatrix(matrix); MatrixUtil.clearMatrix(matrix);
MatrixUtil.embedBasicPatterns(1, matrix); MatrixUtil.embedBasicPatterns(Version.getVersionForNumber(1), matrix);
MatrixUtil.embedDataBits(bits, -1, matrix); MatrixUtil.embedDataBits(bits, -1, matrix);
String expected = String expected =
" 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" + " 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); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.buildMatrix(bits, MatrixUtil.buildMatrix(bits,
ErrorCorrectionLevel.H, ErrorCorrectionLevel.H,
1, // Version 1 Version.getVersionForNumber(1), // Version 1
3, // Mask pattern 3 3, // Mask pattern 3
matrix); matrix);
String expected = String expected =
@ -294,7 +295,7 @@ public final class MatrixUtilTestCase extends Assert {
public void testMakeVersionInfoBits() throws WriterException { public void testMakeVersionInfoBits() throws WriterException {
// From Appendix D in JISX0510:2004 (p 68) // From Appendix D in JISX0510:2004 (p 68)
BitArray bits = new BitArray(); BitArray bits = new BitArray();
MatrixUtil.makeVersionInfoBits(7, bits); MatrixUtil.makeVersionInfoBits(Version.getVersionForNumber(7), bits);
assertEquals(" ...XXXXX ..X..X.X ..", bits.toString()); 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.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode; import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -30,33 +31,18 @@ public final class QRCodeTestCase extends Assert {
@Test @Test
public void test() { public void test() {
QRCode qrCode = new QRCode(); QRCode qrCode = new QRCode();
// Initially the QR Code should be invalid.
assertFalse(qrCode.isValid());
// First, test simple setters and getters. // First, test simple setters and getters.
// We use numbers of version 7-H. // We use numbers of version 7-H.
qrCode.setMode(Mode.BYTE); qrCode.setMode(Mode.BYTE);
qrCode.setECLevel(ErrorCorrectionLevel.H); qrCode.setECLevel(ErrorCorrectionLevel.H);
qrCode.setVersion(7); qrCode.setVersion(Version.getVersionForNumber(7));
qrCode.setMatrixWidth(45);
qrCode.setMaskPattern(3); qrCode.setMaskPattern(3);
qrCode.setNumTotalBytes(196);
qrCode.setNumDataBytes(66);
qrCode.setNumECBytes(130);
qrCode.setNumRSBlocks(5);
assertSame(Mode.BYTE, qrCode.getMode()); assertSame(Mode.BYTE, qrCode.getMode());
assertSame(ErrorCorrectionLevel.H, qrCode.getECLevel()); assertSame(ErrorCorrectionLevel.H, qrCode.getECLevel());
assertEquals(7, qrCode.getVersion()); assertEquals(7, qrCode.getVersion().getVersionNumber());
assertEquals(45, qrCode.getMatrixWidth());
assertEquals(3, qrCode.getMaskPattern()); 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. // Prepare the matrix.
ByteMatrix matrix = new ByteMatrix(45, 45); ByteMatrix matrix = new ByteMatrix(45, 45);
@ -70,16 +56,6 @@ public final class QRCodeTestCase extends Assert {
// Set the matrix. // Set the matrix.
qrCode.setMatrix(matrix); qrCode.setMatrix(matrix);
assertSame(matrix, qrCode.getMatrix()); 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 @Test
@ -89,13 +65,8 @@ public final class QRCodeTestCase extends Assert {
"<<\n" + "<<\n" +
" mode: null\n" + " mode: null\n" +
" ecLevel: null\n" + " ecLevel: null\n" +
" version: -1\n" + " version: null\n" +
" matrixWidth: -1\n" +
" maskPattern: -1\n" + " maskPattern: -1\n" +
" numTotalBytes: -1\n" +
" numDataBytes: -1\n" +
" numECBytes: -1\n" +
" numRSBlocks: -1\n" +
" matrix: null\n" + " matrix: null\n" +
">>\n"; ">>\n";
assertEquals(expected, qrCode.toString()); assertEquals(expected, qrCode.toString());
@ -106,13 +77,8 @@ public final class QRCodeTestCase extends Assert {
QRCode qrCode = new QRCode(); QRCode qrCode = new QRCode();
qrCode.setMode(Mode.BYTE); qrCode.setMode(Mode.BYTE);
qrCode.setECLevel(ErrorCorrectionLevel.H); qrCode.setECLevel(ErrorCorrectionLevel.H);
qrCode.setVersion(1); qrCode.setVersion(Version.getVersionForNumber(1));
qrCode.setMatrixWidth(21);
qrCode.setMaskPattern(3); qrCode.setMaskPattern(3);
qrCode.setNumTotalBytes(26);
qrCode.setNumDataBytes(9);
qrCode.setNumECBytes(17);
qrCode.setNumRSBlocks(1);
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
for (int y = 0; y < 21; ++y) { for (int y = 0; y < 21; ++y) {
for (int x = 0; x < 21; ++x) { for (int x = 0; x < 21; ++x) {
@ -120,17 +86,11 @@ public final class QRCodeTestCase extends Assert {
} }
} }
qrCode.setMatrix(matrix); qrCode.setMatrix(matrix);
assertTrue(qrCode.isValid());
String expected = "<<\n" + String expected = "<<\n" +
" mode: BYTE\n" + " mode: BYTE\n" +
" ecLevel: H\n" + " ecLevel: H\n" +
" version: 1\n" + " version: 1\n" +
" matrixWidth: 21\n" +
" maskPattern: 3\n" + " maskPattern: 3\n" +
" numTotalBytes: 26\n" +
" numDataBytes: 9\n" +
" numECBytes: 17\n" +
" numRSBlocks: 1\n" +
" matrix:\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" + " 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" + " 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1\n" +