mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
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:
parent
99011addb3
commit
c73aac2f90
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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" +
|
||||||
|
|
Loading…
Reference in a new issue