Remove Debug and switch to eceptions in Encoder / Writer API

git-svn-id: https://zxing.googlecode.com/svn/trunk@740 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2008-11-20 12:26:20 +00:00
parent 7b0a66862d
commit 65be5295c8
12 changed files with 309 additions and 446 deletions

View file

@ -24,6 +24,10 @@ package com.google.zxing;
*/ */
public final class WriterException extends Exception { public final class WriterException extends Exception {
public WriterException() {
super();
}
public WriterException(String message) { public WriterException(String message) {
super(message); super(message);
} }

View file

@ -60,11 +60,8 @@ public final class QRCodeWriter implements Writer {
// TODO: Check hints for error correction level instead of hardcoding // TODO: Check hints for error correction level instead of hardcoding
int errorCorrectionLevel = QRCode.EC_LEVEL_L; int errorCorrectionLevel = QRCode.EC_LEVEL_L;
QRCode code = new QRCode(); QRCode code = new QRCode();
if (Encoder.Encode(new ByteArray(contents), errorCorrectionLevel, code)) { Encoder.Encode(new ByteArray(contents), errorCorrectionLevel, code);
return renderResult(code, width, height); return renderResult(code, width, height);
} else {
throw new WriterException("Could not generate a QR Code");
}
} }
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses

View file

@ -39,8 +39,9 @@ public final class BitVector {
// Return the bit value at "index". // Return the bit value at "index".
public int at(final int index) { public int at(final int index) {
Debug.DCHECK_LE(0, index); if (index < 0 || index >= sizeInBits) {
Debug.DCHECK_LT(index, sizeInBits); throw new IllegalArgumentException("Bad index: " + index);
}
final int value = array[index >> 3] & 0xff; final int value = array[index >> 3] & 0xff;
return (value >> (7 - (index & 0x7))) & 1; return (value >> (7 - (index & 0x7))) & 1;
} }
@ -59,7 +60,9 @@ public final class BitVector {
// Append one bit to the bit vector. // Append one bit to the bit vector.
public void AppendBit(final int bit) { public void AppendBit(final int bit) {
Debug.DCHECK(bit == 0 || bit == 1); if (!(bit == 0 || bit == 1)) {
throw new IllegalArgumentException("Bad bit");
}
final int num_bits_in_last_byte = sizeInBits & 0x7; final int num_bits_in_last_byte = sizeInBits & 0x7;
// We'll expand array if we don't have bits in the last byte. // We'll expand array if we don't have bits in the last byte.
if (num_bits_in_last_byte == 0) { if (num_bits_in_last_byte == 0) {
@ -79,7 +82,9 @@ public final class BitVector {
// - AppendBits(0x00, 4) adds 0000. // - AppendBits(0x00, 4) adds 0000.
// - AppendBits(0xff, 8) adds 11111111. // - AppendBits(0xff, 8) adds 11111111.
public void AppendBits(final int value, final int num_bits) { public void AppendBits(final int value, final int num_bits) {
Debug.DCHECK(num_bits >= 0 && num_bits <= 32); if (num_bits < 0 || num_bits > 32) {
throw new IllegalArgumentException("Num bits must be between 0 and 32");
}
int num_bits_left = num_bits; int num_bits_left = num_bits;
while (num_bits_left > 0) { while (num_bits_left > 0) {
// Optimization for byte-oriented appending. // Optimization for byte-oriented appending.
@ -105,7 +110,9 @@ public final class BitVector {
// Modify the bit vector by XOR'ing with "other" // Modify the bit vector by XOR'ing with "other"
public void XOR(final BitVector other) { public void XOR(final BitVector other) {
Debug.DCHECK_EQ(sizeInBits, other.size()); if (sizeInBits != other.size()) {
throw new IllegalArgumentException("BitVector sizes don't match");
}
int sizeInBytes = (sizeInBits + 7) >> 3; int sizeInBytes = (sizeInBits + 7) >> 3;
for (int i = 0; i < sizeInBytes; ++i) { for (int i = 0; i < sizeInBytes; ++i) {
// The last byte could be incomplete (i.e. not have 8 bits in // The last byte could be incomplete (i.e. not have 8 bits in
@ -123,7 +130,7 @@ public final class BitVector {
} else if (at(i) == 1) { } else if (at(i) == 1) {
result.append("1"); result.append("1");
} else { } else {
Debug.DCHECK(false); throw new IllegalArgumentException("Byte isn't 0 or 1");
} }
} }
return result.toString(); return result.toString();

View file

@ -1,67 +0,0 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.qrcode.encoder;
/**
* JAVAPORT: Equivalent methods to the C++ versions. It's debateable whether these should throw or
* assert. We can revisit that decision, or remove these throughout the code later.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public class Debug {
public static void LOG_ERROR(String message) {
// Can't use IllegalStateException unfortunately in J2ME
// TODO do something else with this anyway
throw new RuntimeException(message);
}
public static void LOG_INFO(String message) {
// Chris M -- throwing RuntimeException in an INFO function seems terribly
// strange and violates my assumptions about logging information. Even
// ERROR is borderline since it's effectively going to go unchecked by most
// callers. There has to be a more general way to do this.
// throw new RuntimeException(message);
}
public static void DCHECK(boolean condition) {
if (!condition) {
throw new RuntimeException();
}
}
public static void DCHECK_LT(int a, int b) {
DCHECK(a < b);
}
public static void DCHECK_LE(int a, int b) {
DCHECK(a <= b);
}
public static void DCHECK_GT(int a, int b) {
DCHECK(a > b);
}
public static void DCHECK_GE(int a, int b) {
DCHECK(a >= b);
}
public static void DCHECK_EQ(int a, int b) {
DCHECK(a == b);
}
}

View file

@ -20,6 +20,7 @@ import com.google.zxing.common.ByteMatrix;
import com.google.zxing.common.ByteArray; import com.google.zxing.common.ByteArray;
import com.google.zxing.common.reedsolomon.GF256; import com.google.zxing.common.reedsolomon.GF256;
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
import com.google.zxing.WriterException;
import java.util.Vector; import java.util.Vector;
@ -41,8 +42,8 @@ public final class Encoder {
private static final class RSBlockInfo { private static final class RSBlockInfo {
int num_bytes; final int num_bytes;
int block_info[][]; final int[][] block_info;
public RSBlockInfo(int num_bytes, int[][] block_info) { public RSBlockInfo(int num_bytes, int[][] block_info) {
this.num_bytes = num_bytes; this.num_bytes = num_bytes;
@ -99,8 +100,8 @@ public final class Encoder {
private static final class BlockPair { private static final class BlockPair {
private ByteArray dataBytes; private final ByteArray dataBytes;
private ByteArray errorCorrectionBytes; private final ByteArray errorCorrectionBytes;
public BlockPair(ByteArray data, ByteArray errorCorrection) { public BlockPair(ByteArray data, ByteArray errorCorrection) {
dataBytes = data; dataBytes = data;
@ -125,35 +126,25 @@ 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 boolean Encode(final ByteArray bytes, int ec_level, QRCode qr_code) { public static void Encode(final ByteArray bytes, int ec_level, QRCode qr_code) throws WriterException {
// Step 1: Choose the mode (encoding). // Step 1: Choose the mode (encoding).
final int mode = ChooseMode(bytes); final int mode = ChooseMode(bytes);
// Step 2: Append "bytes" into "data_bits" in appropriate encoding. // Step 2: Append "bytes" into "data_bits" in appropriate encoding.
BitVector data_bits = new BitVector(); BitVector data_bits = new BitVector();
if (!AppendBytes(bytes, mode, data_bits)) { AppendBytes(bytes, mode, data_bits);
return false;
}
// Step 3: Initialize QR code that can contain "data_bits". // Step 3: Initialize QR code that can contain "data_bits".
final int num_input_bytes = data_bits.num_bytes(); final int num_input_bytes = data_bits.num_bytes();
if (!InitQRCode(num_input_bytes, ec_level, mode, qr_code)) { InitQRCode(num_input_bytes, ec_level, mode, qr_code);
return false;
}
// Step 4: Build another bit vector that contains header and data. // Step 4: Build another bit vector that contains header and data.
BitVector header_and_data_bits = new BitVector(); BitVector header_and_data_bits = new BitVector();
if (!AppendModeInfo(qr_code.mode(), header_and_data_bits)) { AppendModeInfo(qr_code.mode(), header_and_data_bits);
return false; AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), header_and_data_bits);
}
if (!AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), header_and_data_bits)) {
return false;
}
header_and_data_bits.AppendBitVector(data_bits); header_and_data_bits.AppendBitVector(data_bits);
// Step 5: Terminate the bits properly. // Step 5: Terminate the bits properly.
if (!TerminateBits(qr_code.num_data_bytes(), header_and_data_bits)) { TerminateBits(qr_code.num_data_bytes(), header_and_data_bits);
return false;
}
// Step 6: Interleave data bits with error correction code. // Step 6: Interleave data bits with error correction code.
BitVector final_bits = new BitVector(); BitVector final_bits = new BitVector();
@ -164,10 +155,6 @@ public final class Encoder {
ByteMatrix matrix = new ByteMatrix(qr_code.matrix_width(), qr_code.matrix_width()); ByteMatrix matrix = new ByteMatrix(qr_code.matrix_width(), qr_code.matrix_width());
qr_code.set_mask_pattern(ChooseMaskPattern(final_bits, qr_code.ec_level(), qr_code.version(), qr_code.set_mask_pattern(ChooseMaskPattern(final_bits, qr_code.ec_level(), qr_code.version(),
matrix)); matrix));
if (qr_code.mask_pattern() == -1) {
// There was an error.
return false;
}
// Step 8. Build the matrix and set it to "qr_code". // Step 8. Build the matrix and set it to "qr_code".
MatrixUtil.BuildMatrix(final_bits, qr_code.ec_level(), qr_code.version(), MatrixUtil.BuildMatrix(final_bits, qr_code.ec_level(), qr_code.version(),
@ -175,10 +162,8 @@ public final class Encoder {
qr_code.set_matrix(matrix); qr_code.set_matrix(matrix);
// Step 9. Make sure we have a valid QR Code. // Step 9. Make sure we have a valid QR Code.
if (!qr_code.IsValid()) { if (!qr_code.IsValid()) {
Debug.LOG_ERROR("Invalid QR code: " + qr_code.toString()); throw new WriterException("Invalid QR code: " + qr_code.toString());
return false;
} }
return true;
} }
// Return the code point of the table used in alphanumeric mode. Return -1 if there is no // Return the code point of the table used in alphanumeric mode. Return -1 if there is no
@ -198,7 +183,7 @@ public final class Encoder {
// interpreted as one character in Shift_JIS, but also two characters in ISO-8859-1. // interpreted as one character in Shift_JIS, but also two characters in ISO-8859-1.
// //
// JAVAPORT: This MODE_KANJI limitation sounds like a problem for us. // JAVAPORT: This MODE_KANJI limitation sounds like a problem for us.
public static int ChooseMode(final ByteArray bytes) { public static int ChooseMode(final ByteArray bytes) throws WriterException {
boolean has_numeric = false; boolean has_numeric = false;
boolean has_alphanumeric = false; boolean has_alphanumeric = false;
boolean has_other = false; boolean has_other = false;
@ -220,15 +205,16 @@ public final class Encoder {
return QRCode.MODE_NUMERIC; return QRCode.MODE_NUMERIC;
} }
// "bytes" must be empty to reach here. // "bytes" must be empty to reach here.
Debug.DCHECK(bytes.empty()); if (!bytes.empty()) {
throw new WriterException("Bytes left over");
}
return QRCode.MODE_8BIT_BYTE; return QRCode.MODE_8BIT_BYTE;
} }
private static int ChooseMaskPattern(final BitVector bits, int ec_level, int version, private static int ChooseMaskPattern(final BitVector bits, int ec_level, int version,
ByteMatrix matrix) { ByteMatrix matrix) throws WriterException {
if (!QRCode.IsValidMatrixWidth(matrix.width())) { if (!QRCode.IsValidMatrixWidth(matrix.width())) {
Debug.LOG_ERROR("Invalid matrix width: " + matrix.width()); throw new WriterException("Invalid matrix width: " + matrix.width());
return -1;
} }
int min_penalty = Integer.MAX_VALUE; // Lower penalty is better. int min_penalty = Integer.MAX_VALUE; // Lower penalty is better.
@ -236,12 +222,9 @@ public final class Encoder {
// We try all mask patterns to choose the best one. // We try all mask patterns to choose the best one.
for (int i = 0; i < QRCode.kNumMaskPatterns; ++i) { for (int i = 0; i < QRCode.kNumMaskPatterns; ++i) {
final int mask_pattern = i; final int mask_pattern = i;
if (!MatrixUtil.BuildMatrix(bits, ec_level, version, MatrixUtil.BuildMatrix(bits, ec_level, version, mask_pattern, matrix);
mask_pattern, matrix)) {
return -1;
}
final int penalty = MaskUtil.CalculateMaskPenalty(matrix); final int penalty = MaskUtil.CalculateMaskPenalty(matrix);
Debug.LOG_INFO("mask_pattern: " + mask_pattern + ", " + "penalty: " + penalty); System.out.println("mask_pattern: " + mask_pattern + ", " + "penalty: " + penalty);
if (penalty < min_penalty) { if (penalty < min_penalty) {
min_penalty = penalty; min_penalty = penalty;
best_mask_pattern = mask_pattern; best_mask_pattern = mask_pattern;
@ -252,13 +235,12 @@ public final class Encoder {
// Initialize "qr_code" according to "num_input_bytes", "ec_level", and "mode". On success, modify // Initialize "qr_code" according to "num_input_bytes", "ec_level", and "mode". On success, modify
// "qr_code" and return true. On error, return false. // "qr_code" and return true. On error, return false.
private static boolean InitQRCode(int num_input_bytes, int ec_level, int mode, QRCode qr_code) { private static void InitQRCode(int num_input_bytes, int ec_level, int mode, QRCode qr_code) throws WriterException {
qr_code.set_ec_level(ec_level); qr_code.set_ec_level(ec_level);
qr_code.set_mode(mode); qr_code.set_mode(mode);
if (!QRCode.IsValidECLevel(ec_level)) { if (!QRCode.IsValidECLevel(ec_level)) {
Debug.LOG_ERROR("Invalid EC level: " + ec_level); throw new WriterException("Invalid EC level: " + ec_level);
return false;
} }
// In the following comments, we use numbers of Version 7-H. // In the following comments, we use numbers of Version 7-H.
@ -285,19 +267,17 @@ public final class Encoder {
qr_code.set_num_ec_bytes(num_bytes - num_data_bytes); qr_code.set_num_ec_bytes(num_bytes - num_data_bytes);
// num_matrix_width = 21 + 6 * 4 = 45 // num_matrix_width = 21 + 6 * 4 = 45
qr_code.set_matrix_width(21 + i * 4); qr_code.set_matrix_width(21 + i * 4);
return true; return;
} }
} }
Debug.LOG_ERROR("Cannot find proper rs block info (input data too big?)"); throw new WriterException("Cannot find proper rs block info (input data too big?)");
return false;
} }
// Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). // Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
static boolean TerminateBits(int num_data_bytes, BitVector bits) { static void TerminateBits(int num_data_bytes, BitVector bits) throws WriterException {
final int capacity = num_data_bytes * 8; final int capacity = num_data_bytes * 8;
if (bits.size() > capacity) { if (bits.size() > capacity) {
Debug.LOG_ERROR("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity); throw new WriterException("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity);
return false;
} }
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
for (int i = 0; i < 4 && bits.size() < capacity; ++i) { for (int i = 0; i < 4 && bits.size() < capacity; ++i) {
@ -312,7 +292,9 @@ public final class Encoder {
} }
} }
// Should be 8-bit aligned here. // Should be 8-bit aligned here.
Debug.DCHECK_EQ(0, bits.size() % 8); if (bits.size() % 8 != 0) {
throw new WriterException("Number of bits is not a multiple of 8");
}
// If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
final int num_padding_bytes = num_data_bytes - bits.num_bytes(); final int num_padding_bytes = num_data_bytes - bits.num_bytes();
for (int i = 0; i < num_padding_bytes; ++i) { for (int i = 0; i < num_padding_bytes; ++i) {
@ -322,8 +304,9 @@ public final class Encoder {
bits.AppendBits(0x11, 8); bits.AppendBits(0x11, 8);
} }
} }
Debug.DCHECK_EQ(bits.size(), capacity); // Should be same. if (bits.size() != capacity) {
return bits.size() == capacity; throw new WriterException("Bits size does not equal capacity");
}
} }
// Get number of data bytes and number of error correction bytes for block id "block_id". Store // Get number of data bytes and number of error correction bytes for block id "block_id". Store
@ -331,8 +314,10 @@ public final class Encoder {
// JISX0510:2004 (p.30) // JISX0510:2004 (p.30)
static void GetNumDataBytesAndNumECBytesForBlockID(int num_total_bytes, int num_data_bytes, static void GetNumDataBytesAndNumECBytesForBlockID(int num_total_bytes, int num_data_bytes,
int num_rs_blocks, int block_id, int[] num_data_bytes_in_block, int num_rs_blocks, int block_id, int[] num_data_bytes_in_block,
int[] num_ec_bytes_in_block) { int[] num_ec_bytes_in_block) throws WriterException {
Debug.DCHECK_LT(block_id, num_rs_blocks); if (block_id >= num_rs_blocks) {
throw new WriterException("Block ID too large");
}
// num_rs_blocks_in_group2 = 196 % 5 = 1 // num_rs_blocks_in_group2 = 196 % 5 = 1
final int num_rs_blocks_in_group2 = num_total_bytes % num_rs_blocks; final int num_rs_blocks_in_group2 = num_total_bytes % num_rs_blocks;
// num_rs_blocks_in_group1 = 5 - 1 = 4 // num_rs_blocks_in_group1 = 5 - 1 = 4
@ -353,15 +338,21 @@ public final class Encoder {
num_data_bytes_in_group2; num_data_bytes_in_group2;
// Sanity checks. // Sanity checks.
// 26 = 26 // 26 = 26
Debug.DCHECK_EQ(num_ec_bytes_in_group1, num_ec_bytes_in_group2); if (num_ec_bytes_in_group1 != num_ec_bytes_in_group2) {
throw new WriterException("EC bytes mismatch");
}
// 5 = 4 + 1. // 5 = 4 + 1.
Debug.DCHECK_EQ(num_rs_blocks, num_rs_blocks_in_group1 + num_rs_blocks_in_group2); if (num_rs_blocks != num_rs_blocks_in_group1 + num_rs_blocks_in_group2) {
throw new WriterException("RS blocks mismatch");
}
// 196 = (13 + 26) * 4 + (14 + 26) * 1 // 196 = (13 + 26) * 4 + (14 + 26) * 1
Debug.DCHECK_EQ(num_total_bytes, if (num_total_bytes !=
((num_data_bytes_in_group1 + num_ec_bytes_in_group1) * ((num_data_bytes_in_group1 + num_ec_bytes_in_group1) *
num_rs_blocks_in_group1) + num_rs_blocks_in_group1) +
((num_data_bytes_in_group2 + num_ec_bytes_in_group2) * ((num_data_bytes_in_group2 + num_ec_bytes_in_group2) *
num_rs_blocks_in_group2)); num_rs_blocks_in_group2)) {
throw new WriterException("Total bytes mismatch");
}
if (block_id < num_rs_blocks_in_group1) { if (block_id < num_rs_blocks_in_group1) {
num_data_bytes_in_block[0] = num_data_bytes_in_group1; num_data_bytes_in_block[0] = num_data_bytes_in_group1;
@ -375,11 +366,13 @@ 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" and return true. On error, return false. The interleave rule is complicated. See 8.6 // "result" and return true. On error, return false. The interleave rule is complicated. See 8.6
// of JISX0510:2004 (p.37) for details. // of JISX0510:2004 (p.37) for details.
static boolean InterleaveWithECBytes(final BitVector bits, int num_total_bytes, static void InterleaveWithECBytes(final BitVector bits, int num_total_bytes,
int num_data_bytes, int num_rs_blocks, BitVector result) { int num_data_bytes, int num_rs_blocks, BitVector result) throws WriterException {
// "bits" must have "num_data_bytes" bytes of data. // "bits" must have "num_data_bytes" bytes of data.
Debug.DCHECK(bits.num_bytes() == num_data_bytes); if (bits.num_bytes() != num_data_bytes) {
throw new WriterException("Number of bits and data bytes does not match");
}
// Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
// store the divided data bytes blocks and error correction bytes blocks into "blocks". // store the divided data bytes blocks and error correction bytes blocks into "blocks".
@ -406,7 +399,9 @@ public final class Encoder {
max_num_ec_bytes = Math.max(max_num_ec_bytes, ec_bytes.size()); max_num_ec_bytes = Math.max(max_num_ec_bytes, ec_bytes.size());
data_bytes_offset += num_data_bytes_in_block[0]; data_bytes_offset += num_data_bytes_in_block[0];
} }
Debug.DCHECK_EQ(num_data_bytes, data_bytes_offset); if (num_data_bytes != data_bytes_offset) {
throw new WriterException("Data bytes does not match offset");
}
// First, place data blocks. // First, place data blocks.
for (int i = 0; i < max_num_data_bytes; ++i) { for (int i = 0; i < max_num_data_bytes; ++i) {
@ -426,12 +421,10 @@ public final class Encoder {
} }
} }
} }
if (num_total_bytes == result.num_bytes()) { // Should be same. if (num_total_bytes != result.num_bytes()) { // Should be same.
return true; throw new WriterException("Interleaving error: " + num_total_bytes + " and " + result.num_bytes() +
}
Debug.LOG_ERROR("Interleaving error: " + num_total_bytes + " and " + result.num_bytes() +
" differ."); " differ.");
return false; }
} }
static ByteArray GenerateECBytes(ByteArray data_bytes, int num_ec_bytes_in_block) { static ByteArray GenerateECBytes(ByteArray data_bytes, int num_ec_bytes_in_block) {
@ -451,67 +444,60 @@ public final class Encoder {
// Append mode info. On success, store the result in "bits" and return true. On error, return // Append mode info. On success, store the result in "bits" and return true. On error, return
// false. // false.
static boolean AppendModeInfo(int mode, BitVector bits) { static void AppendModeInfo(int mode, BitVector bits) throws WriterException {
final int code = QRCode.GetModeCode(mode); final int code = QRCode.GetModeCode(mode);
if (code == -1) {
Debug.LOG_ERROR("Invalid mode: " + mode);
return false;
}
bits.AppendBits(code, 4); bits.AppendBits(code, 4);
return true;
} }
// Append length info. On success, store the result in "bits" and return true. On error, return // Append length info. On success, store the result in "bits" and return true. On error, return
// false. // false.
static boolean AppendLengthInfo(int num_bytes, int version, int mode, BitVector bits) { static void AppendLengthInfo(int num_bytes, int version, int mode, BitVector bits) throws WriterException {
int num_letters = num_bytes; int num_letters = num_bytes;
// In Kanji mode, a letter is represented in two bytes. // In Kanji mode, a letter is represented in two bytes.
if (mode == QRCode.MODE_KANJI) { if (mode == QRCode.MODE_KANJI) {
Debug.DCHECK_EQ(0, num_letters % 2); if (num_letters % 2 != 0) {
throw new WriterException("Number of letters must be even");
}
num_letters /= 2; num_letters /= 2;
} }
final int num_bits = QRCode.GetNumBitsForLength(version, mode); final int num_bits = QRCode.GetNumBitsForLength(version, mode);
if (num_bits == -1) {
Debug.LOG_ERROR("num_bits unset");
return false;
}
if (num_letters > ((1 << num_bits) - 1)) { if (num_letters > ((1 << num_bits) - 1)) {
Debug.LOG_ERROR(num_letters + "is bigger than" + ((1 << num_bits) - 1)); throw new WriterException(num_letters + "is bigger than" + ((1 << num_bits) - 1));
return false;
} }
bits.AppendBits(num_letters, num_bits); bits.AppendBits(num_letters, num_bits);
return true;
} }
// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits" // Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits"
// and return true. On error, return false. // and return true. On error, return false.
static boolean AppendBytes(final ByteArray bytes, int mode, BitVector bits) { static void AppendBytes(final ByteArray bytes, int mode, BitVector bits) throws WriterException {
switch (mode) { switch (mode) {
case QRCode.MODE_NUMERIC: case QRCode.MODE_NUMERIC:
return AppendNumericBytes(bytes, bits); AppendNumericBytes(bytes, bits);
case QRCode.MODE_ALPHANUMERIC:
return AppendAlphanumericBytes(bytes, bits);
case QRCode.MODE_8BIT_BYTE:
return Append8BitBytes(bytes, bits);
case QRCode.MODE_KANJI:
return AppendKanjiBytes(bytes, bits);
default:
break; break;
case QRCode.MODE_ALPHANUMERIC:
AppendAlphanumericBytes(bytes, bits);
break;
case QRCode.MODE_8BIT_BYTE:
Append8BitBytes(bytes, bits);
break;
case QRCode.MODE_KANJI:
AppendKanjiBytes(bytes, bits);
break;
default:
throw new WriterException("Invalid mode: " + mode);
} }
Debug.LOG_ERROR("Invalid mode: " + mode);
return false;
} }
// Append "bytes" to "bits" using QRCode.MODE_NUMERIC mode. On success, store the result in "bits" // Append "bytes" to "bits" using QRCode.MODE_NUMERIC mode. On success, store the result in "bits"
// and return true. On error, return false. // and return true. On error, return false.
static boolean AppendNumericBytes(final ByteArray bytes, BitVector bits) { static void AppendNumericBytes(final ByteArray bytes, BitVector bits) throws WriterException {
// Validate all the bytes first. // Validate all the bytes first.
for (int i = 0; i < bytes.size(); ++i) { for (int i = 0; i < bytes.size(); ++i) {
int oneByte = bytes.at(i); int oneByte = bytes.at(i);
if (oneByte < '0' || oneByte > '9') { if (oneByte < '0' || oneByte > '9') {
return false; throw new WriterException("Non-digit found");
} }
} }
for (int i = 0; i < bytes.size();) { for (int i = 0; i < bytes.size();) {
@ -533,21 +519,20 @@ public final class Encoder {
++i; ++i;
} }
} }
return true;
} }
// Append "bytes" to "bits" using QRCode.MODE_ALPHANUMERIC mode. On success, store the result in // Append "bytes" to "bits" using QRCode.MODE_ALPHANUMERIC mode. On success, store the result in
// "bits" and return true. On error, return false. // "bits" and return true. On error, return false.
static boolean AppendAlphanumericBytes(final ByteArray bytes, BitVector bits) { static void AppendAlphanumericBytes(final ByteArray bytes, BitVector bits) throws WriterException {
for (int i = 0; i < bytes.size();) { for (int i = 0; i < bytes.size();) {
final int code1 = GetAlphanumericCode(bytes.at(i)); final int code1 = GetAlphanumericCode(bytes.at(i));
if (code1 == -1) { if (code1 == -1) {
return false; throw new WriterException();
} }
if (i + 1 < bytes.size()) { if (i + 1 < bytes.size()) {
final int code2 = GetAlphanumericCode(bytes.at(i + 1)); final int code2 = GetAlphanumericCode(bytes.at(i + 1));
if (code2 == -1) { if (code2 == -1) {
return false; throw new WriterException();
} }
// Encode two alphanumeric letters in 11 bits. // Encode two alphanumeric letters in 11 bits.
bits.AppendBits(code1 * 45 + code2, 11); bits.AppendBits(code1 * 45 + code2, 11);
@ -558,29 +543,27 @@ public final class Encoder {
++i; ++i;
} }
} }
return true;
} }
// Append "bytes" to "bits" using QRCode.MODE_8BIT_BYTE mode. On success, store the result in // Append "bytes" to "bits" using QRCode.MODE_8BIT_BYTE mode. On success, store the result in
// "bits" and return true. On error, return false. // "bits" and return true. On error, return false.
static boolean Append8BitBytes(final ByteArray bytes, BitVector bits) { static void Append8BitBytes(final ByteArray bytes, BitVector bits) {
for (int i = 0; i < bytes.size(); ++i) { for (int i = 0; i < bytes.size(); ++i) {
bits.AppendBits(bytes.at(i), 8); bits.AppendBits(bytes.at(i), 8);
} }
return true;
} }
// Append "bytes" to "bits" using QRCode.MODE_KANJI mode. On success, store the result in "bits" // Append "bytes" to "bits" using QRCode.MODE_KANJI mode. On success, store the result in "bits"
// and return true. On error, return false. See 8.4.5 of JISX0510:2004 (p.21) for how to encode // and return true. On error, return false. See 8.4.5 of JISX0510:2004 (p.21) for how to encode
// Kanji bytes. // Kanji bytes.
static boolean AppendKanjiBytes(final ByteArray bytes, BitVector bits) { static void AppendKanjiBytes(final ByteArray bytes, BitVector bits) throws WriterException {
if (bytes.size() % 2 != 0) { if (bytes.size() % 2 != 0) {
// JAVAPORT: Our log implementation throws, which causes the unit test to fail. throw new WriterException("Number of bytes must be even");
//Debug.LOG_ERROR("Invalid byte sequence: " + bytes);
return false;
} }
for (int i = 0; i < bytes.size(); i += 2) { for (int i = 0; i < bytes.size(); i += 2) {
Debug.DCHECK(IsValidKanji(bytes.at(i), bytes.at(i + 1))); if (!IsValidKanji(bytes.at(i), bytes.at(i + 1))) {
throw new WriterException("Invalid Kanji at " + i);
}
final int code = (bytes.at(i) << 8) | bytes.at(i + 1); final int code = (bytes.at(i) << 8) | bytes.at(i + 1);
int subtracted = -1; int subtracted = -1;
if (code >= 0x8140 && code <= 0x9ffc) { if (code >= 0x8140 && code <= 0x9ffc) {
@ -589,13 +572,11 @@ public final class Encoder {
subtracted = code - 0xc140; subtracted = code - 0xc140;
} }
if (subtracted == -1) { if (subtracted == -1) {
Debug.LOG_ERROR("Invalid byte sequence: " + bytes); throw new WriterException("Invalid byte sequence: " + bytes);
return false;
} }
final int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); final int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
bits.AppendBits(encoded, 13); bits.AppendBits(encoded, 13);
} }
return true;
} }
// Check if "byte1" and "byte2" can compose a valid Kanji letter (2-byte Shift_JIS letter). The // Check if "byte1" and "byte2" can compose a valid Kanji letter (2-byte Shift_JIS letter). The

View file

@ -40,7 +40,7 @@ public final class MaskUtil {
public static int ApplyMaskPenaltyRule1(final ByteMatrix matrix) { public static int ApplyMaskPenaltyRule1(final ByteMatrix matrix) {
final int penalty = (ApplyMaskPenaltyRule1Internal(matrix, true) + final int penalty = (ApplyMaskPenaltyRule1Internal(matrix, true) +
ApplyMaskPenaltyRule1Internal(matrix, false)); ApplyMaskPenaltyRule1Internal(matrix, false));
Debug.LOG_INFO("\tApplyMaskPenaltyRule1: " + penalty); System.out.println("\tApplyMaskPenaltyRule1: " + penalty);
return penalty; return penalty;
} }
@ -59,7 +59,7 @@ public final class MaskUtil {
} }
} }
} }
Debug.LOG_INFO("\tApplyMaskPenaltyRule2: " + penalty); System.out.println("\tApplyMaskPenaltyRule2: " + penalty);
return penalty; return penalty;
} }
@ -116,7 +116,7 @@ public final class MaskUtil {
} }
} }
} }
Debug.LOG_INFO("\tApplyMaskPenaltyRule3: " + penalty); System.out.println("\tApplyMaskPenaltyRule3: " + penalty);
return penalty; return penalty;
} }
@ -144,14 +144,16 @@ public final class MaskUtil {
final int num_total_cells = matrix.height() * matrix.width(); final int num_total_cells = matrix.height() * matrix.width();
double dark_ratio = (double) num_dark_cells / num_total_cells; double dark_ratio = (double) num_dark_cells / num_total_cells;
final int penalty = Math.abs((int) (dark_ratio * 100 - 50)) / 5 * 10; final int penalty = Math.abs((int) (dark_ratio * 100 - 50)) / 5 * 10;
Debug.LOG_INFO("\tApplyMaskPenaltyRule4: " + penalty); System.out.println("\tApplyMaskPenaltyRule4: " + penalty);
return penalty; return penalty;
} }
// Return the mask bit for "mask_pattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask // Return the mask bit for "mask_pattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
// pattern conditions. // pattern conditions.
public static int GetDataMaskBit(final int mask_pattern, final int x, final int y) { public static int GetDataMaskBit(final int mask_pattern, final int x, final int y) {
Debug.DCHECK(QRCode.IsValidMaskPattern(mask_pattern)); if (!QRCode.IsValidMaskPattern(mask_pattern)) {
throw new IllegalArgumentException("Invalid mask pattern");
}
switch (mask_pattern) { switch (mask_pattern) {
case 0: case 0:
return ((y + x) % 2 == 0) ? 1 : 0; return ((y + x) % 2 == 0) ? 1 : 0;
@ -169,11 +171,8 @@ public final class MaskUtil {
return ((((y * x) % 2) + ((y * x) % 3)) % 2 == 0) ? 1 : 0; return ((((y * x) % 2) + ((y * x) % 3)) % 2 == 0) ? 1 : 0;
case 7: case 7:
return ((((y * x) % 3) + ((y + x) % 2)) % 2 == 0) ? 1 : 0; return ((((y * x) % 3) + ((y + x) % 2)) % 2 == 0) ? 1 : 0;
default:
;
} }
Debug.LOG_ERROR("invalid mask pattern: " + mask_pattern); throw new IllegalArgumentException("invalid mask pattern: " + mask_pattern);
return -1;
} }
// Helper function for ApplyMaskPenaltyRule1. We need this for doing this calculation in both // Helper function for ApplyMaskPenaltyRule1. We need this for doing this calculation in both

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteMatrix; import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
/** /**
* @author satorux@google.com (Satoru Takabayashi) - creator * @author satorux@google.com (Satoru Takabayashi) - creator
@ -95,7 +96,7 @@ public final class MatrixUtil {
}; };
// Type info cells at the left top corner. // Type info cells at the left top corner.
private static int kTypeInfoCoordinates[][] = { private static final int[][] kTypeInfoCoordinates = {
{8, 0}, {8, 0},
{8, 1}, {8, 1},
{8, 2}, {8, 2},
@ -130,22 +131,16 @@ public final class MatrixUtil {
// Build 2D matrix of QR Code from "data_bits" with "ec_level", "version" and "mask_pattern". On // Build 2D matrix of QR Code from "data_bits" with "ec_level", "version" and "mask_pattern". On
// success, store the result in "matrix" and return true. On error, return false. // success, store the result in "matrix" and return true. On error, return false.
public static boolean BuildMatrix(final BitVector data_bits, int ec_level, int version, public static void BuildMatrix(final BitVector data_bits, int ec_level, int version,
int mask_pattern, ByteMatrix matrix) { int mask_pattern, ByteMatrix matrix) throws WriterException {
MatrixUtil.ClearMatrix(matrix); MatrixUtil.ClearMatrix(matrix);
if (!EmbedBasicPatterns(version, matrix)) { EmbedBasicPatterns(version, matrix);
return false;
}
// Type information appear with any version. // Type information appear with any version.
if (!EmbedTypeInfo(ec_level, mask_pattern, matrix)) { EmbedTypeInfo(ec_level, mask_pattern, matrix);
return false;
}
// Version info appear if version >= 7. // Version info appear if version >= 7.
if (!MaybeEmbedVersionInfo(version, matrix)) { MaybeEmbedVersionInfo(version, matrix);
return false;
}
// Data should be embedded at end. // Data should be embedded at end.
return EmbedDataBits(data_bits, mask_pattern, matrix); EmbedDataBits(data_bits, mask_pattern, matrix);
} }
// Embed basic patterns. On success, modify the matrix and return true. On error, return false. // Embed basic patterns. On success, modify the matrix and return true. On error, return false.
@ -154,7 +149,7 @@ public 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
public static boolean EmbedBasicPatterns(int version, ByteMatrix matrix) { public static void EmbedBasicPatterns(int 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.
@ -164,16 +159,12 @@ public final class MatrixUtil {
MaybeEmbedPositionAdjustmentPatterns(version, matrix); MaybeEmbedPositionAdjustmentPatterns(version, matrix);
// Timing patterns should be embedded after position adj. patterns. // Timing patterns should be embedded after position adj. patterns.
EmbedTimingPatterns(matrix); EmbedTimingPatterns(matrix);
return true;
} }
// Embed type information. On success, modify the matrix and return true. On error, return false. // Embed type information. On success, modify the matrix.
public static boolean EmbedTypeInfo(int ec_level, int mask_pattern, ByteMatrix matrix) { public static void EmbedTypeInfo(int ec_level, int mask_pattern, ByteMatrix matrix) throws WriterException {
BitVector type_info_bits = new BitVector(); BitVector type_info_bits = new BitVector();
if (!MakeTypeInfoBits(ec_level, mask_pattern, type_info_bits)) { MakeTypeInfoBits(ec_level, mask_pattern, type_info_bits);
return false;
}
Debug.DCHECK_EQ(15, type_info_bits.size());
for (int i = 0; i < type_info_bits.size(); ++i) { for (int i = 0; i < type_info_bits.size(); ++i) {
// Place bits in LSB to MSB order. LSB (least significant bit) is the last value in // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
@ -197,22 +188,18 @@ public final class MatrixUtil {
matrix.set(y2, x2, bit); matrix.set(y2, x2, bit);
} }
} }
return true;
} }
// Embed version information if need be. On success, modify the matrix and return true. On error, // Embed version information if need be. On success, modify the matrix and return true. On error,
// return false. See 8.10 of JISX0510:2004 (p.47) for how to embed version information. Return // return false. See 8.10 of JISX0510:2004 (p.47) for how to embed version information. Return
// true on success, otherwise return false. // true on success, otherwise return false.
public static boolean MaybeEmbedVersionInfo(int version, ByteMatrix matrix) { public static void MaybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException {
if (version < 7) { // Version info is necessary if version >= 7. if (version < 7) { // Version info is necessary if version >= 7.
return true; // Don't need version info. return; // Don't need version info.
} }
BitVector version_info_bits = new BitVector(); BitVector version_info_bits = new BitVector();
if (!MakeVersionInfoBits(version, version_info_bits)) { MakeVersionInfoBits(version, version_info_bits);
return false;
}
Debug.DCHECK_EQ(18, version_info_bits.size());
int bit_index = 6 * 3 - 1; // It will decrease from 17 to 0. int bit_index = 6 * 3 - 1; // It will decrease from 17 to 0.
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 3; ++j) { for (int j = 0; j < 3; ++j) {
@ -225,13 +212,13 @@ public final class MatrixUtil {
matrix.set(i, matrix.height() - 11 + j, bit); matrix.set(i, matrix.height() - 11 + j, bit);
} }
} }
return true;
} }
// Embed "data_bits" using "mask_pattern". On success, modify the matrix and return true. On // Embed "data_bits" using "mask_pattern". On success, modify the matrix and return true. On
// error, return false. For debugging purposes, it skips masking process if "mask_pattern" is -1. // error, return false. For debugging purposes, it skips masking process if "mask_pattern" is -1.
// See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
public static boolean EmbedDataBits(final BitVector data_bits, int mask_pattern, ByteMatrix matrix) { public static void EmbedDataBits(final BitVector data_bits, int mask_pattern, ByteMatrix matrix)
throws WriterException {
int bit_index = 0; int bit_index = 0;
int direction = -1; int direction = -1;
// Start from the right bottom cell. // Start from the right bottom cell.
@ -249,7 +236,7 @@ public final class MatrixUtil {
if (!IsEmpty(matrix.get(y, xx))) { if (!IsEmpty(matrix.get(y, xx))) {
continue; continue;
} }
int bit = -1; int bit;
if (bit_index < data_bits.size()) { if (bit_index < data_bits.size()) {
bit = data_bits.at(bit_index); bit = data_bits.at(bit_index);
++bit_index; ++bit_index;
@ -258,12 +245,10 @@ public final class MatrixUtil {
// in 8.4.9 of JISX0510:2004 (p. 24). // in 8.4.9 of JISX0510:2004 (p. 24).
bit = 0; bit = 0;
} }
Debug.DCHECK(IsValidValue(bit));
// Skip masking if mask_pattern is -1. // Skip masking if mask_pattern is -1.
if (mask_pattern != -1) { if (mask_pattern != -1) {
final int mask = MaskUtil.GetDataMaskBit(mask_pattern, xx, y); final int mask = MaskUtil.GetDataMaskBit(mask_pattern, xx, y);
Debug.DCHECK(mask == 0 || mask == 1);
bit ^= mask; bit ^= mask;
} }
matrix.set(y, xx, bit); matrix.set(y, xx, bit);
@ -275,12 +260,9 @@ public final class MatrixUtil {
x -= 2; // Move to the left. x -= 2; // Move to the left.
} }
// All bits should be consumed. // All bits should be consumed.
if (bit_index < data_bits.size()) { if (bit_index != data_bits.size()) {
Debug.LOG_ERROR("Not all bits consumed: " + bit_index + "/" + data_bits.size()); throw new WriterException("Not all bits consumed: " + bit_index + "/" + data_bits.size());
return false;
} }
Debug.DCHECK_EQ(bit_index, data_bits.size());
return true;
} }
// Return the position of the most significant bit set (to one) in the "value". The most // Return the position of the most significant bit set (to one) in the "value". The most
@ -338,13 +320,10 @@ public final class MatrixUtil {
// Make bit vector of type information. On success, store the result in "bits" and return true. // Make bit vector of type information. On success, store the result in "bits" and return true.
// On error, return false. Encode error correction level and mask pattern. See 8.9 of // On error, return false. Encode error correction level and mask pattern. See 8.9 of
// JISX0510:2004 (p.45) for details. // JISX0510:2004 (p.45) for details.
public static boolean MakeTypeInfoBits(int ec_level, final int mask_pattern, BitVector bits) { public static void MakeTypeInfoBits(int ec_level, final int mask_pattern, BitVector bits) throws WriterException {
final int ec_code = QRCode.GetECLevelCode(ec_level); final int ec_code = QRCode.GetECLevelCode(ec_level);
if (ec_code == -1) {
return false;
}
if (!QRCode.IsValidMaskPattern(mask_pattern)) { if (!QRCode.IsValidMaskPattern(mask_pattern)) {
return false; throw new WriterException("Invalid mask pattern");
} }
final int type_info = (ec_code << 3) | mask_pattern; final int type_info = (ec_code << 3) | mask_pattern;
bits.AppendBits(type_info, 5); bits.AppendBits(type_info, 5);
@ -357,24 +336,20 @@ public final class MatrixUtil {
bits.XOR(mask_bits); bits.XOR(mask_bits);
if (bits.size() != 15) { // Just in case. if (bits.size() != 15) { // Just in case.
Debug.LOG_ERROR("should not happen but we got: " + bits.size()); throw new WriterException("should not happen but we got: " + bits.size());
return false;
} }
return true;
} }
// 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.
// On error, return false. See 8.10 of JISX0510:2004 (p.45) for details. // On error, return false. See 8.10 of JISX0510:2004 (p.45) for details.
public static boolean MakeVersionInfoBits(int version, BitVector bits) { public static void MakeVersionInfoBits(int version, BitVector bits) throws WriterException {
bits.AppendBits(version, 6); bits.AppendBits(version, 6);
final int bch_code = MatrixUtil.CalculateBCHCode(version, kVersionInfoPoly); final int bch_code = MatrixUtil.CalculateBCHCode(version, kVersionInfoPoly);
bits.AppendBits(bch_code, 12); bits.AppendBits(bch_code, 12);
if (bits.size() != 18) { // Just in case. if (bits.size() != 18) { // Just in case.
Debug.LOG_ERROR("should not happen but we got: " + bits.size()); throw new WriterException("should not happen but we got: " + bits.size());
return false;
} }
return true;
} }
// Check if "value" is empty. // Check if "value" is empty.
@ -389,18 +364,22 @@ public final class MatrixUtil {
value == 1); // Dark (black). value == 1); // Dark (black).
} }
private static void EmbedTimingPatterns(ByteMatrix matrix) { 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.width() - 8; ++i) { for (int i = 8; i < matrix.width() - 8; ++i) {
final int bit = (i + 1) % 2; final int bit = (i + 1) % 2;
// Horizontal line. // Horizontal line.
Debug.DCHECK(IsValidValue(matrix.get(6, i))); 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);
} }
// Vertical line. // Vertical line.
Debug.DCHECK(IsValidValue(matrix.get(i, 6))); 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);
} }
@ -408,29 +387,37 @@ public final class MatrixUtil {
} }
// Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46) // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
private static void EmbedDarkDotAtLeftBottomCorner(ByteMatrix matrix) { private static void EmbedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
Debug.DCHECK(matrix.get(matrix.height() - 8, 8) != 0); if (matrix.get(matrix.height() - 8, 8) == 0) {
throw new WriterException();
}
matrix.set(matrix.height() - 8, 8, 1); matrix.set(matrix.height() - 8, 8, 1);
} }
private static void EmbedHorizontalSeparationPattern(final int x_start, final int y_start, private static void EmbedHorizontalSeparationPattern(final int x_start, final int y_start,
ByteMatrix matrix) { ByteMatrix matrix) throws WriterException {
// We know the width and height. // We know the width and height.
Debug.DCHECK_EQ(8, kHorizontalSeparationPattern[0].length); if (kHorizontalSeparationPattern[0].length != 8 || kHorizontalSeparationPattern.length != 1) {
Debug.DCHECK_EQ(1, kHorizontalSeparationPattern.length); throw new WriterException("Bad horizontal separation pattern");
}
for (int x = 0; x < 8; ++x) { for (int x = 0; x < 8; ++x) {
Debug.DCHECK(IsEmpty(matrix.get(y_start, x_start + x))); if (!IsEmpty(matrix.get(y_start, x_start + x))) {
throw new WriterException();
}
matrix.set(y_start, x_start + x, kHorizontalSeparationPattern[0][x]); matrix.set(y_start, x_start + x, kHorizontalSeparationPattern[0][x]);
} }
} }
private static void EmbedVerticalSeparationPattern(final int x_start, final int y_start, private static void EmbedVerticalSeparationPattern(final int x_start, final int y_start,
ByteMatrix matrix) { ByteMatrix matrix) throws WriterException {
// We know the width and height. // We know the width and height.
Debug.DCHECK_EQ(1, kVerticalSeparationPattern[0].length); if (kVerticalSeparationPattern[0].length != 1 || kVerticalSeparationPattern.length != 7) {
Debug.DCHECK_EQ(7, kVerticalSeparationPattern.length); throw new WriterException("Bad vertical separation pattern");
}
for (int y = 0; y < 7; ++y) { for (int y = 0; y < 7; ++y) {
Debug.DCHECK(IsEmpty(matrix.get(y_start + y, x_start))); if (!IsEmpty(matrix.get(y_start + y, x_start))) {
throw new WriterException();
}
matrix.set(y_start + y, x_start, kVerticalSeparationPattern[y][0]); matrix.set(y_start + y, x_start, kVerticalSeparationPattern[y][0]);
} }
} }
@ -439,33 +426,39 @@ public final class MatrixUtil {
// 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(final int x_start, final int y_start, private static void EmbedPositionAdjustmentPattern(final int x_start, final int y_start,
ByteMatrix matrix) { ByteMatrix matrix) throws WriterException {
// We know the width and height. // We know the width and height.
Debug.DCHECK_EQ(5, kPositionAdjustmentPattern[0].length); if (kPositionAdjustmentPattern[0].length != 5 || kPositionAdjustmentPattern.length != 5) {
Debug.DCHECK_EQ(5, kPositionAdjustmentPattern.length); 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) {
Debug.DCHECK(IsEmpty(matrix.get(y_start + y, x_start + x))); if (!IsEmpty(matrix.get(y_start + y, x_start + x))) {
throw new WriterException();
}
matrix.set(y_start + y, x_start + x, kPositionAdjustmentPattern[y][x]); matrix.set(y_start + y, x_start + x, kPositionAdjustmentPattern[y][x]);
} }
} }
} }
private static void EmbedPositionDetectionPattern(final int x_start, final int y_start, private static void EmbedPositionDetectionPattern(final int x_start, final int y_start,
ByteMatrix matrix) { ByteMatrix matrix) throws WriterException {
// We know the width and height. // We know the width and height.
Debug.DCHECK_EQ(7, kPositionDetectionPattern[0].length); if (kPositionDetectionPattern[0].length != 7 || kPositionDetectionPattern.length != 7) {
Debug.DCHECK_EQ(7, kPositionDetectionPattern.length); 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) {
Debug.DCHECK(IsEmpty(matrix.get(y_start + y, x_start + x))); if (!IsEmpty(matrix.get(y_start + y, x_start + x))) {
throw new WriterException();
}
matrix.set(y_start + y, x_start + x, kPositionDetectionPattern[y][x]); matrix.set(y_start + y, x_start + x, kPositionDetectionPattern[y][x]);
} }
} }
} }
// Embed position detection patterns and surrounding vertical/horizontal separators. // Embed position detection patterns and surrounding vertical/horizontal separators.
private static void EmbedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) { private static void EmbedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {
// Embed three big squares at corners. // Embed three big squares at corners.
final int pdp_width = kPositionDetectionPattern[0].length; final int pdp_width = kPositionDetectionPattern[0].length;
// Left top corner. // Left top corner.
@ -497,7 +490,7 @@ public final class MatrixUtil {
} }
// Embed position adjustment patterns if need be. // Embed position adjustment patterns if need be.
private static void MaybeEmbedPositionAdjustmentPatterns(final int version, ByteMatrix matrix) { private static void MaybeEmbedPositionAdjustmentPatterns(final int version, ByteMatrix matrix) throws WriterException {
if (version < 2) { // The patterns appear if version >= 2 if (version < 2) { // The patterns appear if version >= 2
return; return;
} }

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteMatrix; import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
/** /**
* @author satorux@google.com (Satoru Takabayashi) - creator * @author satorux@google.com (Satoru Takabayashi) - creator
@ -25,11 +26,11 @@ import com.google.zxing.common.ByteMatrix;
public final class QRCode { public final class QRCode {
// Magic numbers. // Magic numbers.
public static final int kMinVersion = 1; private static final int kMinVersion = 1;
public static final int kMaxVersion = 40; private static final int kMaxVersion = 40;
// For matrix width, see 7.3.1 of JISX0510:2004 (p.5). // For matrix width, see 7.3.1 of JISX0510:2004 (p.5).
public static final int kMinMatrixWidth = 21; // Version 1 private static final int kMinMatrixWidth = 21; // Version 1
public static final int kMaxMatrixWidth = 177; // Version 40 (21 + 4 * (40 -1)). private static final int kMaxMatrixWidth = 177; // Version 40 (21 + 4 * (40 -1)).
public static final int kNumMaskPatterns = 8; public static final int kNumMaskPatterns = 8;
// See table 3 of JISX0510:2004 (p.16) // See table 3 of JISX0510:2004 (p.16)
@ -128,7 +129,10 @@ public final class QRCode {
public int at(int x, int y) { public int at(int x, int y) {
// The value must be zero or one. // The value must be zero or one.
int value = matrix_.get(y, x); int value = matrix_.get(y, x);
Debug.DCHECK(value == 0 || value == 1); if (!(value == 0 || value == 1)) {
// this is really like an assert... not sure what better exception to use?
throw new RuntimeException("Bad value");
}
return value; return value;
} }
@ -300,7 +304,7 @@ public final class QRCode {
// Return the code of error correction level. On error, return -1. The codes of error correction // Return the code of error correction level. On error, return -1. The codes of error correction
// levels are defined in the table 22 of JISX0510:2004 (p.45). // levels are defined in the table 22 of JISX0510:2004 (p.45).
public static int GetECLevelCode(final int ec_level) { public static int GetECLevelCode(final int ec_level) throws WriterException {
switch (ec_level) { switch (ec_level) {
case QRCode.EC_LEVEL_L: case QRCode.EC_LEVEL_L:
return 1; return 1;
@ -311,14 +315,13 @@ public final class QRCode {
case QRCode.EC_LEVEL_H: case QRCode.EC_LEVEL_H:
return 2; return 2;
default: default:
break; throw new WriterException("Unknown EC level");
} }
return -1; // Unknown error correction level.
} }
// Return the code of mode. On error, return -1. The codes of modes are defined in the table 2 of // Return the code of mode. On error, return -1. The codes of modes are defined in the table 2 of
// JISX0510:2004 (p.16). // JISX0510:2004 (p.16).
public static int GetModeCode(final int mode) { public static int GetModeCode(final int mode) throws WriterException {
switch (mode) { switch (mode) {
case QRCode.MODE_NUMERIC: case QRCode.MODE_NUMERIC:
return 1; return 1;
@ -329,21 +332,18 @@ public final class QRCode {
case QRCode.MODE_KANJI: case QRCode.MODE_KANJI:
return 8; return 8;
default: default:
break; throw new WriterException("Unknown mode: " + mode);
} }
return -1; // Unknown mode.
} }
// Return the number of bits needed for representing the length info of QR Code with "version" and // Return the number of bits needed for representing the length info of QR Code with "version" and
// "mode". On error, return -1. // "mode". On error, return -1.
public static int GetNumBitsForLength(int version, int mode) { static int GetNumBitsForLength(int version, int mode) {
if (!IsValidVersion(version)) { if (!IsValidVersion(version)) {
Debug.LOG_ERROR("Invalid version: " + version); throw new IllegalArgumentException("Invalid version: " + version);
return -1;
} }
if (!IsValidMode(mode)) { if (!IsValidMode(mode)) {
Debug.LOG_ERROR("Invalid mode: " + mode); throw new IllegalArgumentException("Invalid mode: " + mode);
return -1;
} }
if (version >= 1 && version <= 9) { if (version >= 1 && version <= 9) {
return kNumBitsTable[0][mode]; return kNumBitsTable[0][mode];
@ -351,10 +351,8 @@ public final class QRCode {
return kNumBitsTable[1][mode]; return kNumBitsTable[1][mode];
} else if (version >= 27 && version <= 40) { } else if (version >= 27 && version <= 40) {
return kNumBitsTable[2][mode]; return kNumBitsTable[2][mode];
} else {
Debug.LOG_ERROR("Should not reach");
} }
return -1; throw new IllegalArgumentException("Bad version: " + version);
} }
// Return true if the all values in the matrix are binary numbers. Otherwise, return false. // Return true if the all values in the matrix are binary numbers. Otherwise, return false.

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteArray; import com.google.zxing.common.ByteArray;
import com.google.zxing.WriterException;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
@ -25,7 +26,7 @@ import junit.framework.TestCase;
*/ */
public final class EncoderTestCase extends TestCase { public final class EncoderTestCase extends TestCase {
public void testGetAlphanumericCode() { public void testGetAlphanumericCode() throws WriterException {
// The first ten code points are numbers. // The first ten code points are numbers.
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
assertEquals(i, Encoder.GetAlphanumericCode('0' + i)); assertEquals(i, Encoder.GetAlphanumericCode('0' + i));
@ -53,7 +54,7 @@ public final class EncoderTestCase extends TestCase {
assertEquals(-1, Encoder.GetAlphanumericCode('\0')); assertEquals(-1, Encoder.GetAlphanumericCode('\0'));
} }
public void testChooseMode() { public void testChooseMode() throws WriterException {
// Numeric mode. // Numeric mode.
assertEquals(QRCode.MODE_NUMERIC, Encoder.ChooseMode(new ByteArray("0"))); assertEquals(QRCode.MODE_NUMERIC, Encoder.ChooseMode(new ByteArray("0")));
assertEquals(QRCode.MODE_NUMERIC, Encoder.ChooseMode(new ByteArray("0123456789"))); assertEquals(QRCode.MODE_NUMERIC, Encoder.ChooseMode(new ByteArray("0123456789")));
@ -82,9 +83,9 @@ public final class EncoderTestCase extends TestCase {
assertEquals(QRCode.MODE_8BIT_BYTE, Encoder.ChooseMode(new ByteArray(dat3))); assertEquals(QRCode.MODE_8BIT_BYTE, Encoder.ChooseMode(new ByteArray(dat3)));
} }
public void testEncode() { public void testEncode() throws WriterException {
QRCode qr_code = new QRCode(); QRCode qr_code = new QRCode();
assertTrue(Encoder.Encode(new ByteArray("ABCDEF"), QRCode.EC_LEVEL_H, qr_code)); Encoder.Encode(new ByteArray("ABCDEF"), QRCode.EC_LEVEL_H, qr_code);
// The following is a valid QR Code that can be read by cell phones. // The following is a valid QR Code that can be read by cell phones.
String expected = String expected =
"<<\n" + "<<\n" +
@ -123,85 +124,99 @@ public final class EncoderTestCase extends TestCase {
assertEquals(expected, qr_code.toString()); assertEquals(expected, qr_code.toString());
} }
public void testAppendModeInfo() { public void testAppendModeInfo() throws WriterException {
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendModeInfo(QRCode.MODE_NUMERIC, bits)); Encoder.AppendModeInfo(QRCode.MODE_NUMERIC, bits);
assertEquals("0001", bits.toString()); assertEquals("0001", bits.toString());
} }
public void testAppendLengthInfo() { public void testAppendLengthInfo() throws WriterException {
{ {
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(1, // 1 letter (1/1). Encoder.AppendLengthInfo(1, // 1 letter (1/1).
1, // version 1. 1, // version 1.
QRCode.MODE_NUMERIC, QRCode.MODE_NUMERIC,
bits)); bits);
assertEquals("0000000001", bits.toString()); // 10 bits. assertEquals("0000000001", bits.toString()); // 10 bits.
} }
{ {
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(2, // 2 letters (2/1). Encoder.AppendLengthInfo(2, // 2 letters (2/1).
10, // version 10. 10, // version 10.
QRCode.MODE_ALPHANUMERIC, QRCode.MODE_ALPHANUMERIC,
bits)); bits);
assertEquals("00000000010", bits.toString()); // 11 bits. assertEquals("00000000010", bits.toString()); // 11 bits.
} }
{ {
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(255, // 255 letter (255/1). Encoder.AppendLengthInfo(255, // 255 letter (255/1).
27, // version 27. 27, // version 27.
QRCode.MODE_8BIT_BYTE, QRCode.MODE_8BIT_BYTE,
bits)); bits);
assertEquals("0000000011111111", bits.toString()); // 16 bits. assertEquals("0000000011111111", bits.toString()); // 16 bits.
} }
{ {
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(1024, // 512 letters (1024/2). Encoder.AppendLengthInfo(1024, // 512 letters (1024/2).
40, // version 40. 40, // version 40.
QRCode.MODE_KANJI, QRCode.MODE_KANJI,
bits)); bits);
assertEquals("001000000000", bits.toString()); // 12 bits. assertEquals("001000000000", bits.toString()); // 12 bits.
} }
} }
public void testAppendBytes() { public void testAppendBytes() throws WriterException {
{ {
// Should use AppendNumericBytes. // Should use AppendNumericBytes.
// 1 = 01 = 0001 in 4 bits. // 1 = 01 = 0001 in 4 bits.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendBytes(new ByteArray("1"), QRCode.MODE_NUMERIC, bits)); Encoder.AppendBytes(new ByteArray("1"), QRCode.MODE_NUMERIC, bits);
assertEquals("0001" , bits.toString()); assertEquals("0001" , bits.toString());
// 'A' cannot be encoded in MODE_NUMERIC. // 'A' cannot be encoded in MODE_NUMERIC.
assertFalse(Encoder.AppendBytes(new ByteArray("A"), QRCode.MODE_NUMERIC, bits)); try {
Encoder.AppendBytes(new ByteArray("A"), QRCode.MODE_NUMERIC, bits);
fail("Should have thrown exception");
} catch (WriterException we) {
// good
}
} }
{ {
// Should use AppendAlphanumericBytes. // Should use AppendAlphanumericBytes.
// A = 10 = 0xa = 001010 in 6 bits // A = 10 = 0xa = 001010 in 6 bits
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendBytes(new ByteArray("A"), QRCode.MODE_ALPHANUMERIC, bits)); Encoder.AppendBytes(new ByteArray("A"), QRCode.MODE_ALPHANUMERIC, bits);
assertEquals("001010" , bits.toString()); assertEquals("001010" , bits.toString());
// Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC. // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC.
assertFalse(Encoder.AppendBytes(new ByteArray("a"), QRCode.MODE_ALPHANUMERIC, bits)); try {
Encoder.AppendBytes(new ByteArray("a"), QRCode.MODE_ALPHANUMERIC, bits);
} catch (WriterException we) {
// good
}
} }
{ {
// Should use Append8BitBytes. // Should use Append8BitBytes.
// 0x61, 0x62, 0x63 // 0x61, 0x62, 0x63
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendBytes(new ByteArray("abc"), QRCode.MODE_8BIT_BYTE, bits)); Encoder.AppendBytes(new ByteArray("abc"), QRCode.MODE_8BIT_BYTE, bits);
assertEquals("01100001" + "01100010" + "01100011", bits.toString()); assertEquals("01100001" + "01100010" + "01100011", bits.toString());
// Anything can be encoded in QRCode.MODE_8BIT_BYTE. // Anything can be encoded in QRCode.MODE_8BIT_BYTE.
byte[] bytes = {0x00}; byte[] bytes = {0x00};
assertTrue(Encoder.AppendBytes(new ByteArray(bytes), QRCode.MODE_8BIT_BYTE, bits)); Encoder.AppendBytes(new ByteArray(bytes), QRCode.MODE_8BIT_BYTE, bits);
} }
{ {
// Should use AppendKanjiBytes. // Should use AppendKanjiBytes.
// 0x93, 0x5f // 0x93, 0x5f
BitVector bits = new BitVector(); BitVector bits = new BitVector();
byte[] bytes = {(byte)0x93,0x5f}; byte[] bytes = {(byte)0x93,0x5f};
assertTrue(Encoder.AppendBytes(new ByteArray(bytes), QRCode.MODE_KANJI, bits)); Encoder.AppendBytes(new ByteArray(bytes), QRCode.MODE_KANJI, bits);
assertEquals("0110110011111", bits.toString()); assertEquals("0110110011111", bits.toString());
// ASCII characters can not be encoded in QRCode.MODE_KANJI. // ASCII characters can not be encoded in QRCode.MODE_KANJI.
assertFalse(Encoder.AppendBytes(new ByteArray("a"), QRCode.MODE_KANJI, bits));
try {
Encoder.AppendBytes(new ByteArray("a"), QRCode.MODE_KANJI, bits);
} catch (WriterException we) {
// good
}
} }
} }
@ -209,49 +224,49 @@ public final class EncoderTestCase extends TestCase {
// TODO: should be implemented. // TODO: should be implemented.
} }
public void testTerminateBits() { public void testTerminateBits() throws WriterException {
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
assertTrue(Encoder.TerminateBits(0, v)); Encoder.TerminateBits(0, v);
assertEquals("", v.toString()); assertEquals("", v.toString());
} }
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
assertTrue(Encoder.TerminateBits(1, v)); Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString()); assertEquals("00000000", v.toString());
} }
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
v.AppendBits(0, 3); // Append 000 v.AppendBits(0, 3); // Append 000
assertTrue(Encoder.TerminateBits(1, v)); Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString()); assertEquals("00000000", v.toString());
} }
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
v.AppendBits(0, 5); // Append 00000 v.AppendBits(0, 5); // Append 00000
assertTrue(Encoder.TerminateBits(1, v)); Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString()); assertEquals("00000000", v.toString());
} }
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
v.AppendBits(0, 8); // Append 00000000 v.AppendBits(0, 8); // Append 00000000
assertTrue(Encoder.TerminateBits(1, v)); Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString()); assertEquals("00000000", v.toString());
} }
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
assertTrue(Encoder.TerminateBits(2, v)); Encoder.TerminateBits(2, v);
assertEquals("0000000011101100", v.toString()); assertEquals("0000000011101100", v.toString());
} }
{ {
BitVector v = new BitVector(); BitVector v = new BitVector();
v.AppendBits(0, 1); // Append 0 v.AppendBits(0, 1); // Append 0
assertTrue(Encoder.TerminateBits(3, v)); Encoder.TerminateBits(3, v);
assertEquals("000000001110110000010001", v.toString()); assertEquals("000000001110110000010001", v.toString());
} }
} }
public void testGetNumDataBytesAndNumECBytesForBlockID() { public void testGetNumDataBytesAndNumECBytesForBlockID() throws WriterException {
int[] num_data_bytes = new int[1]; int[] num_data_bytes = new int[1];
int[] num_ec_bytes = new int[1]; int[] num_ec_bytes = new int[1];
// Version 1-H. // Version 1-H.
@ -287,7 +302,7 @@ public final class EncoderTestCase extends TestCase {
assertEquals(30, num_ec_bytes[0]); assertEquals(30, num_ec_bytes[0]);
} }
public void testInterleaveWithECBytes() { public void testInterleaveWithECBytes() throws WriterException {
{ {
final byte[] data_bytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236}; final byte[] data_bytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236};
BitVector in = new BitVector(); BitVector in = new BitVector();
@ -295,7 +310,7 @@ public final class EncoderTestCase extends TestCase {
in.AppendBits(data_byte, 8); in.AppendBits(data_byte, 8);
} }
BitVector out = new BitVector(); BitVector out = new BitVector();
assertTrue(Encoder.InterleaveWithECBytes(in, 26, 9, 1, out)); Encoder.InterleaveWithECBytes(in, 26, 9, 1, out);
final byte[] expected = { final 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,
@ -325,7 +340,7 @@ public final class EncoderTestCase extends TestCase {
in.AppendBits(data_byte, 8); in.AppendBits(data_byte, 8);
} }
BitVector out = new BitVector(); BitVector out = new BitVector();
assertTrue(Encoder.InterleaveWithECBytes(in, 134, 62, 4, out)); Encoder.InterleaveWithECBytes(in, 134, 62, 4, out);
final byte[] expected = { final 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,
@ -351,100 +366,108 @@ public final class EncoderTestCase extends TestCase {
} }
} }
public void testAppendNumericBytes() { public void testAppendNumericBytes() throws WriterException {
{ {
// 1 = 01 = 0001 in 4 bits. // 1 = 01 = 0001 in 4 bits.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("1"), bits)); Encoder.AppendNumericBytes(new ByteArray("1"), bits);
assertEquals("0001" , bits.toString()); assertEquals("0001" , bits.toString());
} }
{ {
// 12 = 0xc = 0001100 in 7 bits. // 12 = 0xc = 0001100 in 7 bits.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("12"), bits)); Encoder.AppendNumericBytes(new ByteArray("12"), bits);
assertEquals("0001100" , bits.toString()); assertEquals("0001100" , bits.toString());
} }
{ {
// 123 = 0x7b = 0001111011 in 10 bits. // 123 = 0x7b = 0001111011 in 10 bits.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("123"), bits)); Encoder.AppendNumericBytes(new ByteArray("123"), bits);
assertEquals("0001111011" , bits.toString()); assertEquals("0001111011" , bits.toString());
} }
{ {
// 1234 = "123" + "4" = 0001111011 + 0100 // 1234 = "123" + "4" = 0001111011 + 0100
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("1234"), bits)); Encoder.AppendNumericBytes(new ByteArray("1234"), bits);
assertEquals("0001111011" + "0100" , bits.toString()); assertEquals("0001111011" + "0100" , bits.toString());
} }
{ {
// Empty. // Empty.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray(""), bits)); Encoder.AppendNumericBytes(new ByteArray(""), bits);
assertEquals("" , bits.toString()); assertEquals("" , bits.toString());
} }
{ {
// Invalid data. // Invalid data.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertFalse(Encoder.AppendNumericBytes(new ByteArray("abc"), bits)); try {
Encoder.AppendNumericBytes(new ByteArray("abc"), bits);
} catch (WriterException we) {
// good
}
} }
} }
public void testAppendAlphanumericBytes() { public void testAppendAlphanumericBytes() throws WriterException {
{ {
// A = 10 = 0xa = 001010 in 6 bits // A = 10 = 0xa = 001010 in 6 bits
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray("A"), bits)); Encoder.AppendAlphanumericBytes(new ByteArray("A"), bits);
assertEquals("001010" , bits.toString()); assertEquals("001010" , bits.toString());
} }
{ {
// AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray("AB"), bits)); Encoder.AppendAlphanumericBytes(new ByteArray("AB"), bits);
assertEquals("00111001101", bits.toString()); assertEquals("00111001101", bits.toString());
} }
{ {
// ABC = "AB" + "C" = 00111001101 + 001100 // ABC = "AB" + "C" = 00111001101 + 001100
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray("ABC"), bits)); Encoder.AppendAlphanumericBytes(new ByteArray("ABC"), bits);
assertEquals("00111001101" + "001100" , bits.toString()); assertEquals("00111001101" + "001100" , bits.toString());
} }
{ {
// Empty. // Empty.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray(""), bits)); Encoder.AppendAlphanumericBytes(new ByteArray(""), bits);
assertEquals("" , bits.toString()); assertEquals("" , bits.toString());
} }
{ {
// Invalid data. // Invalid data.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertFalse(Encoder.AppendAlphanumericBytes(new ByteArray("abc"), bits)); try {
Encoder.AppendAlphanumericBytes(new ByteArray("abc"), bits);
} catch (WriterException we) {
// good
}
} }
} }
public void testAppend8BitBytes() { public void testAppend8BitBytes() throws WriterException {
{ {
// 0x61, 0x62, 0x63 // 0x61, 0x62, 0x63
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.Append8BitBytes(new ByteArray("abc"), bits)); Encoder.Append8BitBytes(new ByteArray("abc"), bits);
assertEquals("01100001" + "01100010" + "01100011", bits.toString()); assertEquals("01100001" + "01100010" + "01100011", bits.toString());
} }
{ {
// Empty. // Empty.
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(Encoder.Append8BitBytes(new ByteArray(""), bits)); Encoder.Append8BitBytes(new ByteArray(""), bits);
assertEquals("", bits.toString()); assertEquals("", bits.toString());
} }
} }
// Numbers are from page 21 of JISX0510:2004 // Numbers are from page 21 of JISX0510:2004
public void testAppendKanjiBytes() { public void testAppendKanjiBytes() throws WriterException {
{ {
BitVector bits = new BitVector(); BitVector bits = new BitVector();
byte[] dat1 = {(byte)0x93,0x5f}; byte[] dat1 = {(byte)0x93,0x5f};
assertTrue(Encoder.AppendKanjiBytes(new ByteArray(dat1), bits)); Encoder.AppendKanjiBytes(new ByteArray(dat1), bits);
assertEquals("0110110011111", bits.toString()); assertEquals("0110110011111", bits.toString());
byte[] dat2 = {(byte)0xe4,(byte)0xaa}; byte[] dat2 = {(byte)0xe4,(byte)0xaa};
assertTrue(Encoder.AppendKanjiBytes(new ByteArray(dat2), bits)); Encoder.AppendKanjiBytes(new ByteArray(dat2), bits);
assertEquals("0110110011111" + "1101010101010", bits.toString()); assertEquals("0110110011111" + "1101010101010", bits.toString());
} }
} }

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteMatrix; import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
@ -48,7 +49,7 @@ public final class MatrixUtilTestCase extends TestCase {
assertEquals(-1, matrix.get(1, 1)); assertEquals(-1, matrix.get(1, 1));
} }
public void testEmbedBasicPatterns() { public void testEmbedBasicPatterns() throws WriterException {
{ {
// Version 1. // Version 1.
String expected = String expected =
@ -75,7 +76,7 @@ public final class MatrixUtilTestCase extends TestCase {
" 1 1 1 1 1 1 1 0 \n"; " 1 1 1 1 1 1 1 0 \n";
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.ClearMatrix(matrix); MatrixUtil.ClearMatrix(matrix);
assertTrue(MatrixUtil.EmbedBasicPatterns(1, matrix)); MatrixUtil.EmbedBasicPatterns(1, matrix);
assertEquals(expected, matrix.toString()); assertEquals(expected, matrix.toString());
} }
{ {
@ -109,12 +110,12 @@ public final class MatrixUtilTestCase extends TestCase {
" 1 1 1 1 1 1 1 0 \n"; " 1 1 1 1 1 1 1 0 \n";
ByteMatrix matrix = new ByteMatrix(25, 25); ByteMatrix matrix = new ByteMatrix(25, 25);
MatrixUtil.ClearMatrix(matrix); MatrixUtil.ClearMatrix(matrix);
assertTrue(MatrixUtil.EmbedBasicPatterns(2, matrix)); MatrixUtil.EmbedBasicPatterns(2, matrix);
assertEquals(expected, matrix.toString()); assertEquals(expected, matrix.toString());
} }
} }
public void testEmbedTypeInfo() { public void testEmbedTypeInfo() throws WriterException {
// Type info bits = 100000011001110. // Type info bits = 100000011001110.
String expected = String expected =
" 0 \n" + " 0 \n" +
@ -140,12 +141,11 @@ public final class MatrixUtilTestCase extends TestCase {
" 1 \n"; " 1 \n";
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.ClearMatrix(matrix); MatrixUtil.ClearMatrix(matrix);
boolean info_okay = MatrixUtil.EmbedTypeInfo(QRCode.EC_LEVEL_M, 5, matrix); MatrixUtil.EmbedTypeInfo(QRCode.EC_LEVEL_M, 5, matrix);
assertTrue(info_okay);
assertEquals(expected, matrix.toString()); assertEquals(expected, matrix.toString());
} }
public void testEmbedVersionInfo() { public void testEmbedVersionInfo() throws WriterException {
// Version info bits = 000111 110010 010100 // Version info bits = 000111 110010 010100
String expected = String expected =
" 0 0 1 \n" + " 0 0 1 \n" +
@ -173,11 +173,11 @@ public final class MatrixUtilTestCase extends TestCase {
// 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);
assertTrue(MatrixUtil.MaybeEmbedVersionInfo(7, matrix)); MatrixUtil.MaybeEmbedVersionInfo(7, matrix);
assertEquals(expected, matrix.toString()); assertEquals(expected, matrix.toString());
} }
public void testEmbedDataBits() { public void testEmbedDataBits() throws WriterException {
// Cells other than basic patterns should be filled with zero. // Cells other than basic patterns should be filled with zero.
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" +
@ -205,11 +205,11 @@ public final class MatrixUtilTestCase extends TestCase {
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
MatrixUtil.ClearMatrix(matrix); MatrixUtil.ClearMatrix(matrix);
MatrixUtil.EmbedBasicPatterns(1, matrix); MatrixUtil.EmbedBasicPatterns(1, matrix);
assertTrue(MatrixUtil.EmbedDataBits(bits, -1, matrix)); MatrixUtil.EmbedDataBits(bits, -1, matrix);
assertEquals(expected, matrix.toString()); assertEquals(expected, matrix.toString());
} }
public void testBuildMatrix() { public void testBuildMatrix() throws WriterException {
// From http://www.swetake.com/qr/qr7.html // From http://www.swetake.com/qr/qr7.html
String expected = String expected =
" 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n" + " 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n" +
@ -241,11 +241,11 @@ public final class MatrixUtilTestCase extends TestCase {
bits.AppendBits(c, 8); bits.AppendBits(c, 8);
} }
ByteMatrix matrix = new ByteMatrix(21, 21); ByteMatrix matrix = new ByteMatrix(21, 21);
assertTrue(MatrixUtil.BuildMatrix(bits, MatrixUtil.BuildMatrix(bits,
QRCode.EC_LEVEL_H, QRCode.EC_LEVEL_H,
1, // Version 1 1, // Version 1
3, // Mask pattern 3 3, // Mask pattern 3
matrix)); matrix);
} }
public void testFindMSBSet() { public void testFindMSBSet() {
@ -277,20 +277,20 @@ public final class MatrixUtilTestCase extends TestCase {
// We don't test a lot of cases in this function since we've already // We don't test a lot of cases in this function since we've already
// tested them in TEST(CalculateBCHCode). // tested them in TEST(CalculateBCHCode).
public void testMakeVersionInfoBits() { public void testMakeVersionInfoBits() throws WriterException {
// From Appendix D in JISX0510:2004 (p 68) // From Appendix D in JISX0510:2004 (p 68)
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(MatrixUtil.MakeVersionInfoBits(7, bits)); MatrixUtil.MakeVersionInfoBits(7, bits);
assertEquals("000111110010010100", bits.toString()); assertEquals("000111110010010100", bits.toString());
} }
// We don't test a lot of cases in this function since we've already // We don't test a lot of cases in this function since we've already
// tested them in TEST(CalculateBCHCode). // tested them in TEST(CalculateBCHCode).
public void testMakeTypeInfoInfoBits() { public void testMakeTypeInfoInfoBits() throws WriterException {
// From Appendix C in JISX0510:2004 (p 65) // From Appendix C in JISX0510:2004 (p 65)
BitVector bits = new BitVector(); BitVector bits = new BitVector();
assertTrue(MatrixUtil.MakeTypeInfoBits(QRCode.EC_LEVEL_M, MatrixUtil.MakeTypeInfoBits(QRCode.EC_LEVEL_M,
5, bits)); 5, bits);
assertEquals("100000011001110", bits.toString()); assertEquals("100000011001110", bits.toString());
} }
} }

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteMatrix; import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
@ -208,19 +209,29 @@ public final class QRCodeTestCase extends TestCase {
assertEquals("UNKNOWN", QRCode.ECLevelToString(QRCode.NUM_EC_LEVELS)); assertEquals("UNKNOWN", QRCode.ECLevelToString(QRCode.NUM_EC_LEVELS));
} }
public void testGetModeCode() { public void testGetModeCode() throws WriterException {
assertEquals(1, QRCode.GetModeCode(QRCode.MODE_NUMERIC)); assertEquals(1, QRCode.GetModeCode(QRCode.MODE_NUMERIC));
assertEquals(2, QRCode.GetModeCode(QRCode.MODE_ALPHANUMERIC)); assertEquals(2, QRCode.GetModeCode(QRCode.MODE_ALPHANUMERIC));
assertEquals(4, QRCode.GetModeCode(QRCode.MODE_8BIT_BYTE)); assertEquals(4, QRCode.GetModeCode(QRCode.MODE_8BIT_BYTE));
assertEquals(8, QRCode.GetModeCode(QRCode.MODE_KANJI)); assertEquals(8, QRCode.GetModeCode(QRCode.MODE_KANJI));
assertEquals(-1, QRCode.GetModeCode(QRCode.MODE_UNDEFINED)); try {
QRCode.GetModeCode(QRCode.MODE_UNDEFINED);
fail("Should have thrown exception");
} catch (WriterException we) {
// good
}
} }
public void testGetECLevelCode() { public void testGetECLevelCode() throws WriterException {
assertEquals(1, QRCode.GetECLevelCode(QRCode.EC_LEVEL_L)); assertEquals(1, QRCode.GetECLevelCode(QRCode.EC_LEVEL_L));
assertEquals(0, QRCode.GetECLevelCode(QRCode.EC_LEVEL_M)); assertEquals(0, QRCode.GetECLevelCode(QRCode.EC_LEVEL_M));
assertEquals(3, QRCode.GetECLevelCode(QRCode.EC_LEVEL_Q)); assertEquals(3, QRCode.GetECLevelCode(QRCode.EC_LEVEL_Q));
assertEquals(2, QRCode.GetECLevelCode(QRCode.EC_LEVEL_H)); assertEquals(2, QRCode.GetECLevelCode(QRCode.EC_LEVEL_H));
assertEquals(-1, QRCode.GetECLevelCode(QRCode.EC_LEVEL_UNDEFINED)); try {
QRCode.GetECLevelCode(QRCode.EC_LEVEL_UNDEFINED);
fail("Should have thrown exception");
} catch (WriterException we) {
// good
}
} }
} }

View file

@ -1,83 +0,0 @@
/**
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.qrcode.encoder;
import junit.framework.TestCase;
//#include "file/base/file.h"
//#include "testing/base/gunit.h"
//#include "testing/base/benchmark.h"
//#include "wireless/qrcode/qrcode.h"
//#include "wireless/qrcode/qrcode_encoder.h"
//#include "wireless/qrcode/qrcode_renderer.h"
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author mysen@google.com (Chris Mysen) - ported from C++
*/
public final class RendererTestCase extends TestCase {
// public void testRenderAsPNG() {
// QRCode qr_code = new QRCode();
// assertTrue(Encoder.Encode(new ByteArray("http://www.google.com/"),
// QRCode.EC_LEVEL_M, qr_code));
// String result;
// assertTrue(Renderer.RenderAsPNG(qr_code, 3, result));
// assertFalse(result.length() == 0);
// // We don't test the result image in this test. We do that in
// // RegressionTest().
// }
//
// public void testRenderAsPNGFromData() {
// QRCode qr_code = new QRCode();
// assertTrue(Encoder.Encode(new ByteArray("http://www.google.com/"),
// QRCode.EC_LEVEL_M, qr_code));
// String result1;
// assertTrue(Renderer.RenderAsPNG(qr_code, 3, result1));
//
// String result2;
// assertTrue(Renderer.RenderAsPNGFromData("http://www.google.com/",
// QRCode.EC_LEVEL_M, 3,
// result2));
// assertEquals(result1, result2);
// }
//
// // ec_level comes from QRCode.EC_LEVEL_[LMQH]
// static boolean Compare(final String bytes, final int ec_level,
// final int cell_size, final String golden_base_name) {
// String result;
// assertTrue(Renderer.RenderAsPNGFromData(bytes, ec_level,
// cell_size, result));
// String golden_file_name = "test/data/qrcode_encode/" +
// golden_base_name;
// String golden;
// File.ReadFileToStringOrDie(golden_file_name, golden);
// return golden == result;
// }
//
// // Golden images are generated with "qrcode_sample.cc". The images
// // are checked with both eye balls and cell phones.
// public void testRegressionTest() {
// assertTrue(Compare("http://www.google.com/", QRCode.EC_LEVEL_M, 3,
// "renderer-test-01.png"));
// assertTrue(Compare("12345", QRCode.EC_LEVEL_L, 2,
// "renderer-test-02.png"));
// // Test in Katakana in Shift_JIS.
// byte[] dat = {(byte)0x83,0x65,(byte)0x83,0x58,(byte)0x83,0x67};
// assertTrue(Compare(new String(dat), QRCode.EC_LEVEL_H, 5,
// "renderer-test-03.png"));
// }
}