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 WriterException() {
super();
}
public WriterException(String 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
int errorCorrectionLevel = QRCode.EC_LEVEL_L;
QRCode code = new QRCode();
if (Encoder.Encode(new ByteArray(contents), errorCorrectionLevel, code)) {
return renderResult(code, width, height);
} else {
throw new WriterException("Could not generate a QR Code");
}
Encoder.Encode(new ByteArray(contents), errorCorrectionLevel, code);
return renderResult(code, width, height);
}
// 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".
public int at(final int index) {
Debug.DCHECK_LE(0, index);
Debug.DCHECK_LT(index, sizeInBits);
if (index < 0 || index >= sizeInBits) {
throw new IllegalArgumentException("Bad index: " + index);
}
final int value = array[index >> 3] & 0xff;
return (value >> (7 - (index & 0x7))) & 1;
}
@ -59,7 +60,9 @@ public final class BitVector {
// Append one bit to the bit vector.
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;
// We'll expand array if we don't have bits in the last byte.
if (num_bits_in_last_byte == 0) {
@ -79,7 +82,9 @@ public final class BitVector {
// - AppendBits(0x00, 4) adds 0000.
// - AppendBits(0xff, 8) adds 11111111.
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;
while (num_bits_left > 0) {
// Optimization for byte-oriented appending.
@ -105,7 +110,9 @@ public final class BitVector {
// Modify the bit vector by XOR'ing with "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;
for (int i = 0; i < sizeInBytes; ++i) {
// 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) {
result.append("1");
} else {
Debug.DCHECK(false);
throw new IllegalArgumentException("Byte isn't 0 or 1");
}
}
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.reedsolomon.GF256;
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
import com.google.zxing.WriterException;
import java.util.Vector;
@ -41,8 +42,8 @@ public final class Encoder {
private static final class RSBlockInfo {
int num_bytes;
int block_info[][];
final int num_bytes;
final int[][] block_info;
public RSBlockInfo(int num_bytes, int[][] block_info) {
this.num_bytes = num_bytes;
@ -99,8 +100,8 @@ public final class Encoder {
private static final class BlockPair {
private ByteArray dataBytes;
private ByteArray errorCorrectionBytes;
private final ByteArray dataBytes;
private final ByteArray errorCorrectionBytes;
public BlockPair(ByteArray data, ByteArray errorCorrection) {
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()
// 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).
final int mode = ChooseMode(bytes);
// Step 2: Append "bytes" into "data_bits" in appropriate encoding.
BitVector data_bits = new BitVector();
if (!AppendBytes(bytes, mode, data_bits)) {
return false;
}
AppendBytes(bytes, mode, data_bits);
// Step 3: Initialize QR code that can contain "data_bits".
final int num_input_bytes = data_bits.num_bytes();
if (!InitQRCode(num_input_bytes, ec_level, mode, qr_code)) {
return false;
}
InitQRCode(num_input_bytes, ec_level, mode, qr_code);
// Step 4: Build another bit vector that contains header and data.
BitVector header_and_data_bits = new BitVector();
if (!AppendModeInfo(qr_code.mode(), header_and_data_bits)) {
return false;
}
if (!AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), header_and_data_bits)) {
return false;
}
AppendModeInfo(qr_code.mode(), header_and_data_bits);
AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), header_and_data_bits);
header_and_data_bits.AppendBitVector(data_bits);
// Step 5: Terminate the bits properly.
if (!TerminateBits(qr_code.num_data_bytes(), header_and_data_bits)) {
return false;
}
TerminateBits(qr_code.num_data_bytes(), header_and_data_bits);
// Step 6: Interleave data bits with error correction code.
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());
qr_code.set_mask_pattern(ChooseMaskPattern(final_bits, qr_code.ec_level(), qr_code.version(),
matrix));
if (qr_code.mask_pattern() == -1) {
// There was an error.
return false;
}
// Step 8. Build the matrix and set it to "qr_code".
MatrixUtil.BuildMatrix(final_bits, qr_code.ec_level(), qr_code.version(),
@ -175,10 +162,8 @@ public final class Encoder {
qr_code.set_matrix(matrix);
// Step 9. Make sure we have a valid QR Code.
if (!qr_code.IsValid()) {
Debug.LOG_ERROR("Invalid QR code: " + qr_code.toString());
return false;
throw new WriterException("Invalid QR code: " + qr_code.toString());
}
return true;
}
// 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.
//
// 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_alphanumeric = false;
boolean has_other = false;
@ -220,15 +205,16 @@ public final class Encoder {
return QRCode.MODE_NUMERIC;
}
// "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;
}
private static int ChooseMaskPattern(final BitVector bits, int ec_level, int version,
ByteMatrix matrix) {
ByteMatrix matrix) throws WriterException {
if (!QRCode.IsValidMatrixWidth(matrix.width())) {
Debug.LOG_ERROR("Invalid matrix width: " + matrix.width());
return -1;
throw new WriterException("Invalid matrix width: " + matrix.width());
}
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.
for (int i = 0; i < QRCode.kNumMaskPatterns; ++i) {
final int mask_pattern = i;
if (!MatrixUtil.BuildMatrix(bits, ec_level, version,
mask_pattern, matrix)) {
return -1;
}
MatrixUtil.BuildMatrix(bits, ec_level, version, mask_pattern, 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) {
min_penalty = penalty;
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
// "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_mode(mode);
if (!QRCode.IsValidECLevel(ec_level)) {
Debug.LOG_ERROR("Invalid EC level: " + ec_level);
return false;
throw new WriterException("Invalid EC level: " + ec_level);
}
// 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);
// num_matrix_width = 21 + 6 * 4 = 45
qr_code.set_matrix_width(21 + i * 4);
return true;
return;
}
}
Debug.LOG_ERROR("Cannot find proper rs block info (input data too big?)");
return false;
throw new WriterException("Cannot find proper rs block info (input data too big?)");
}
// 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;
if (bits.size() > capacity) {
Debug.LOG_ERROR("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity);
return false;
throw new WriterException("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity);
}
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
for (int i = 0; i < 4 && bits.size() < capacity; ++i) {
@ -312,7 +292,9 @@ public final class Encoder {
}
}
// 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).
final int num_padding_bytes = num_data_bytes - bits.num_bytes();
for (int i = 0; i < num_padding_bytes; ++i) {
@ -322,8 +304,9 @@ public final class Encoder {
bits.AppendBits(0x11, 8);
}
}
Debug.DCHECK_EQ(bits.size(), capacity); // Should be same.
return bits.size() == capacity;
if (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
@ -331,8 +314,10 @@ public final class Encoder {
// JISX0510:2004 (p.30)
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_ec_bytes_in_block) {
Debug.DCHECK_LT(block_id, num_rs_blocks);
int[] num_ec_bytes_in_block) throws WriterException {
if (block_id >= num_rs_blocks) {
throw new WriterException("Block ID too large");
}
// num_rs_blocks_in_group2 = 196 % 5 = 1
final int num_rs_blocks_in_group2 = num_total_bytes % num_rs_blocks;
// num_rs_blocks_in_group1 = 5 - 1 = 4
@ -353,15 +338,21 @@ public final class Encoder {
num_data_bytes_in_group2;
// Sanity checks.
// 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.
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
Debug.DCHECK_EQ(num_total_bytes,
if (num_total_bytes !=
((num_data_bytes_in_group1 + num_ec_bytes_in_group1) *
num_rs_blocks_in_group1) +
((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) {
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
// "result" and return true. On error, return false. The interleave rule is complicated. See 8.6
// of JISX0510:2004 (p.37) for details.
static boolean InterleaveWithECBytes(final BitVector bits, int num_total_bytes,
int num_data_bytes, int num_rs_blocks, BitVector result) {
static void InterleaveWithECBytes(final BitVector bits, int num_total_bytes,
int num_data_bytes, int num_rs_blocks, BitVector result) throws WriterException {
// "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
// 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());
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.
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.
return true;
}
Debug.LOG_ERROR("Interleaving error: " + num_total_bytes + " and " + result.num_bytes() +
if (num_total_bytes != result.num_bytes()) { // Should be same.
throw new WriterException("Interleaving error: " + num_total_bytes + " and " + result.num_bytes() +
" differ.");
return false;
}
}
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
// false.
static boolean AppendModeInfo(int mode, BitVector bits) {
static void AppendModeInfo(int mode, BitVector bits) throws WriterException {
final int code = QRCode.GetModeCode(mode);
if (code == -1) {
Debug.LOG_ERROR("Invalid mode: " + mode);
return false;
}
bits.AppendBits(code, 4);
return true;
}
// Append length info. On success, store the result in "bits" and return true. On error, return
// 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;
// In Kanji mode, a letter is represented in two bytes.
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;
}
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)) {
Debug.LOG_ERROR(num_letters + "is bigger than" + ((1 << num_bits) - 1));
return false;
throw new WriterException(num_letters + "is bigger than" + ((1 << num_bits) - 1));
}
bits.AppendBits(num_letters, num_bits);
return true;
}
// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits"
// 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) {
case QRCode.MODE_NUMERIC:
return 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:
AppendNumericBytes(bytes, bits);
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"
// 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.
for (int i = 0; i < bytes.size(); ++i) {
int oneByte = bytes.at(i);
if (oneByte < '0' || oneByte > '9') {
return false;
throw new WriterException("Non-digit found");
}
}
for (int i = 0; i < bytes.size();) {
@ -533,21 +519,20 @@ public final class Encoder {
++i;
}
}
return true;
}
// Append "bytes" to "bits" using QRCode.MODE_ALPHANUMERIC mode. On success, store the result in
// "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();) {
final int code1 = GetAlphanumericCode(bytes.at(i));
if (code1 == -1) {
return false;
throw new WriterException();
}
if (i + 1 < bytes.size()) {
final int code2 = GetAlphanumericCode(bytes.at(i + 1));
if (code2 == -1) {
return false;
throw new WriterException();
}
// Encode two alphanumeric letters in 11 bits.
bits.AppendBits(code1 * 45 + code2, 11);
@ -558,29 +543,27 @@ public final class Encoder {
++i;
}
}
return true;
}
// Append "bytes" to "bits" using QRCode.MODE_8BIT_BYTE mode. On success, store the result in
// "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) {
bits.AppendBits(bytes.at(i), 8);
}
return true;
}
// 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
// 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) {
// JAVAPORT: Our log implementation throws, which causes the unit test to fail.
//Debug.LOG_ERROR("Invalid byte sequence: " + bytes);
return false;
throw new WriterException("Number of bytes must be even");
}
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);
int subtracted = -1;
if (code >= 0x8140 && code <= 0x9ffc) {
@ -589,13 +572,11 @@ public final class Encoder {
subtracted = code - 0xc140;
}
if (subtracted == -1) {
Debug.LOG_ERROR("Invalid byte sequence: " + bytes);
return false;
throw new WriterException("Invalid byte sequence: " + bytes);
}
final int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
bits.AppendBits(encoded, 13);
}
return true;
}
// 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) {
final int penalty = (ApplyMaskPenaltyRule1Internal(matrix, true) +
ApplyMaskPenaltyRule1Internal(matrix, false));
Debug.LOG_INFO("\tApplyMaskPenaltyRule1: " + penalty);
System.out.println("\tApplyMaskPenaltyRule1: " + penalty);
return penalty;
}
@ -59,7 +59,7 @@ public final class MaskUtil {
}
}
}
Debug.LOG_INFO("\tApplyMaskPenaltyRule2: " + penalty);
System.out.println("\tApplyMaskPenaltyRule2: " + penalty);
return penalty;
}
@ -116,7 +116,7 @@ public final class MaskUtil {
}
}
}
Debug.LOG_INFO("\tApplyMaskPenaltyRule3: " + penalty);
System.out.println("\tApplyMaskPenaltyRule3: " + penalty);
return penalty;
}
@ -144,14 +144,16 @@ public final class MaskUtil {
final int num_total_cells = matrix.height() * matrix.width();
double dark_ratio = (double) num_dark_cells / num_total_cells;
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 the mask bit for "mask_pattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
// pattern conditions.
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) {
case 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;
case 7:
return ((((y * x) % 3) + ((y + x) % 2)) % 2 == 0) ? 1 : 0;
default:
;
}
Debug.LOG_ERROR("invalid mask pattern: " + mask_pattern);
return -1;
throw new IllegalArgumentException("invalid mask pattern: " + mask_pattern);
}
// 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;
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
@ -95,7 +96,7 @@ public final class MatrixUtil {
};
// Type info cells at the left top corner.
private static int kTypeInfoCoordinates[][] = {
private static final int[][] kTypeInfoCoordinates = {
{8, 0},
{8, 1},
{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
// 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,
int mask_pattern, ByteMatrix matrix) {
public static void BuildMatrix(final BitVector data_bits, int ec_level, int version,
int mask_pattern, ByteMatrix matrix) throws WriterException {
MatrixUtil.ClearMatrix(matrix);
if (!EmbedBasicPatterns(version, matrix)) {
return false;
}
EmbedBasicPatterns(version, matrix);
// Type information appear with any version.
if (!EmbedTypeInfo(ec_level, mask_pattern, matrix)) {
return false;
}
EmbedTypeInfo(ec_level, mask_pattern, matrix);
// Version info appear if version >= 7.
if (!MaybeEmbedVersionInfo(version, matrix)) {
return false;
}
MaybeEmbedVersionInfo(version, matrix);
// 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.
@ -154,7 +149,7 @@ public final class MatrixUtil {
// - Timing patterns
// - Dark dot at the left bottom corner
// - 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.
EmbedPositionDetectionPatternsAndSeparators(matrix);
// Then, embed the dark dot at the left bottom corner.
@ -164,16 +159,12 @@ public final class MatrixUtil {
MaybeEmbedPositionAdjustmentPatterns(version, matrix);
// Timing patterns should be embedded after position adj. patterns.
EmbedTimingPatterns(matrix);
return true;
}
// Embed type information. On success, modify the matrix and return true. On error, return false.
public static boolean EmbedTypeInfo(int ec_level, int mask_pattern, ByteMatrix matrix) {
// Embed type information. On success, modify the matrix.
public static void EmbedTypeInfo(int ec_level, int mask_pattern, ByteMatrix matrix) throws WriterException {
BitVector type_info_bits = new BitVector();
if (!MakeTypeInfoBits(ec_level, mask_pattern, type_info_bits)) {
return false;
}
Debug.DCHECK_EQ(15, type_info_bits.size());
MakeTypeInfoBits(ec_level, mask_pattern, type_info_bits);
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
@ -197,22 +188,18 @@ public final class MatrixUtil {
matrix.set(y2, x2, bit);
}
}
return true;
}
// 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
// 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.
return true; // Don't need version info.
return; // Don't need version info.
}
BitVector version_info_bits = new BitVector();
if (!MakeVersionInfoBits(version, version_info_bits)) {
return false;
}
MakeVersionInfoBits(version, version_info_bits);
Debug.DCHECK_EQ(18, version_info_bits.size());
int bit_index = 6 * 3 - 1; // It will decrease from 17 to 0.
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 3; ++j) {
@ -225,13 +212,13 @@ public final class MatrixUtil {
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
// 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.
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 direction = -1;
// Start from the right bottom cell.
@ -249,7 +236,7 @@ public final class MatrixUtil {
if (!IsEmpty(matrix.get(y, xx))) {
continue;
}
int bit = -1;
int bit;
if (bit_index < data_bits.size()) {
bit = data_bits.at(bit_index);
++bit_index;
@ -258,12 +245,10 @@ public final class MatrixUtil {
// in 8.4.9 of JISX0510:2004 (p. 24).
bit = 0;
}
Debug.DCHECK(IsValidValue(bit));
// Skip masking if mask_pattern is -1.
if (mask_pattern != -1) {
final int mask = MaskUtil.GetDataMaskBit(mask_pattern, xx, y);
Debug.DCHECK(mask == 0 || mask == 1);
bit ^= mask;
}
matrix.set(y, xx, bit);
@ -275,12 +260,9 @@ public final class MatrixUtil {
x -= 2; // Move to the left.
}
// All bits should be consumed.
if (bit_index < data_bits.size()) {
Debug.LOG_ERROR("Not all bits consumed: " + bit_index + "/" + data_bits.size());
return false;
if (bit_index != data_bits.size()) {
throw new WriterException("Not all bits consumed: " + bit_index + "/" + data_bits.size());
}
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
@ -338,13 +320,10 @@ public final class MatrixUtil {
// 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
// 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);
if (ec_code == -1) {
return false;
}
if (!QRCode.IsValidMaskPattern(mask_pattern)) {
return false;
throw new WriterException("Invalid mask pattern");
}
final int type_info = (ec_code << 3) | mask_pattern;
bits.AppendBits(type_info, 5);
@ -357,24 +336,20 @@ public final class MatrixUtil {
bits.XOR(mask_bits);
if (bits.size() != 15) { // Just in case.
Debug.LOG_ERROR("should not happen but we got: " + bits.size());
return false;
throw new WriterException("should not happen but we got: " + bits.size());
}
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.
public static boolean MakeVersionInfoBits(int version, BitVector bits) {
public static void MakeVersionInfoBits(int version, BitVector bits) throws WriterException {
bits.AppendBits(version, 6);
final int bch_code = MatrixUtil.CalculateBCHCode(version, kVersionInfoPoly);
bits.AppendBits(bch_code, 12);
if (bits.size() != 18) { // Just in case.
Debug.LOG_ERROR("should not happen but we got: " + bits.size());
return false;
throw new WriterException("should not happen but we got: " + bits.size());
}
return true;
}
// Check if "value" is empty.
@ -389,18 +364,22 @@ public final class MatrixUtil {
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
// separation patterns (size 1). Thus, 8 = 7 + 1.
for (int i = 8; i < matrix.width() - 8; ++i) {
final int bit = (i + 1) % 2;
// Horizontal line.
Debug.DCHECK(IsValidValue(matrix.get(6, i)));
if (!IsValidValue(matrix.get(6, i))) {
throw new WriterException();
}
if (IsEmpty(matrix.get(6, i))) {
matrix.set(6, i, bit);
}
// Vertical line.
Debug.DCHECK(IsValidValue(matrix.get(i, 6)));
if (!IsValidValue(matrix.get(i, 6))) {
throw new WriterException();
}
if (IsEmpty(matrix.get(i, 6))) {
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)
private static void EmbedDarkDotAtLeftBottomCorner(ByteMatrix matrix) {
Debug.DCHECK(matrix.get(matrix.height() - 8, 8) != 0);
private static void EmbedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
if (matrix.get(matrix.height() - 8, 8) == 0) {
throw new WriterException();
}
matrix.set(matrix.height() - 8, 8, 1);
}
private static void EmbedHorizontalSeparationPattern(final int x_start, final int y_start,
ByteMatrix matrix) {
ByteMatrix matrix) throws WriterException {
// We know the width and height.
Debug.DCHECK_EQ(8, kHorizontalSeparationPattern[0].length);
Debug.DCHECK_EQ(1, kHorizontalSeparationPattern.length);
if (kHorizontalSeparationPattern[0].length != 8 || kHorizontalSeparationPattern.length != 1) {
throw new WriterException("Bad horizontal separation pattern");
}
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]);
}
}
private static void EmbedVerticalSeparationPattern(final int x_start, final int y_start,
ByteMatrix matrix) {
ByteMatrix matrix) throws WriterException {
// We know the width and height.
Debug.DCHECK_EQ(1, kVerticalSeparationPattern[0].length);
Debug.DCHECK_EQ(7, kVerticalSeparationPattern.length);
if (kVerticalSeparationPattern[0].length != 1 || kVerticalSeparationPattern.length != 7) {
throw new WriterException("Bad vertical separation pattern");
}
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]);
}
}
@ -439,33 +426,39 @@ public final class MatrixUtil {
// almost identical, since we cannot write a function that takes 2D arrays in different sizes in
// C/C++. We should live with the fact.
private static void EmbedPositionAdjustmentPattern(final int x_start, final int y_start,
ByteMatrix matrix) {
ByteMatrix matrix) throws WriterException {
// We know the width and height.
Debug.DCHECK_EQ(5, kPositionAdjustmentPattern[0].length);
Debug.DCHECK_EQ(5, kPositionAdjustmentPattern.length);
if (kPositionAdjustmentPattern[0].length != 5 || kPositionAdjustmentPattern.length != 5) {
throw new WriterException("Bad position adjustment");
}
for (int y = 0; y < 5; ++y) {
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]);
}
}
}
private static void EmbedPositionDetectionPattern(final int x_start, final int y_start,
ByteMatrix matrix) {
ByteMatrix matrix) throws WriterException {
// We know the width and height.
Debug.DCHECK_EQ(7, kPositionDetectionPattern[0].length);
Debug.DCHECK_EQ(7, kPositionDetectionPattern.length);
if (kPositionDetectionPattern[0].length != 7 || kPositionDetectionPattern.length != 7) {
throw new WriterException("Bad position detection pattern");
}
for (int y = 0; y < 7; ++y) {
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]);
}
}
}
// 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.
final int pdp_width = kPositionDetectionPattern[0].length;
// Left top corner.
@ -497,7 +490,7 @@ public final class MatrixUtil {
}
// 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
return;
}

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
@ -25,11 +26,11 @@ import com.google.zxing.common.ByteMatrix;
public final class QRCode {
// Magic numbers.
public static final int kMinVersion = 1;
public static final int kMaxVersion = 40;
private static final int kMinVersion = 1;
private static final int kMaxVersion = 40;
// For matrix width, see 7.3.1 of JISX0510:2004 (p.5).
public static final int kMinMatrixWidth = 21; // Version 1
public static final int kMaxMatrixWidth = 177; // Version 40 (21 + 4 * (40 -1)).
private static final int kMinMatrixWidth = 21; // Version 1
private static final int kMaxMatrixWidth = 177; // Version 40 (21 + 4 * (40 -1)).
public static final int kNumMaskPatterns = 8;
// See table 3 of JISX0510:2004 (p.16)
@ -128,7 +129,10 @@ public final class QRCode {
public int at(int x, int y) {
// The value must be zero or one.
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;
}
@ -300,7 +304,7 @@ public final class QRCode {
// 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).
public static int GetECLevelCode(final int ec_level) {
public static int GetECLevelCode(final int ec_level) throws WriterException {
switch (ec_level) {
case QRCode.EC_LEVEL_L:
return 1;
@ -311,14 +315,13 @@ public final class QRCode {
case QRCode.EC_LEVEL_H:
return 2;
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
// JISX0510:2004 (p.16).
public static int GetModeCode(final int mode) {
public static int GetModeCode(final int mode) throws WriterException {
switch (mode) {
case QRCode.MODE_NUMERIC:
return 1;
@ -329,21 +332,18 @@ public final class QRCode {
case QRCode.MODE_KANJI:
return 8;
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
// "mode". On error, return -1.
public static int GetNumBitsForLength(int version, int mode) {
static int GetNumBitsForLength(int version, int mode) {
if (!IsValidVersion(version)) {
Debug.LOG_ERROR("Invalid version: " + version);
return -1;
throw new IllegalArgumentException("Invalid version: " + version);
}
if (!IsValidMode(mode)) {
Debug.LOG_ERROR("Invalid mode: " + mode);
return -1;
throw new IllegalArgumentException("Invalid mode: " + mode);
}
if (version >= 1 && version <= 9) {
return kNumBitsTable[0][mode];
@ -351,10 +351,8 @@ public final class QRCode {
return kNumBitsTable[1][mode];
} else if (version >= 27 && version <= 40) {
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.

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteArray;
import com.google.zxing.WriterException;
import junit.framework.TestCase;
/**
@ -25,7 +26,7 @@ import junit.framework.TestCase;
*/
public final class EncoderTestCase extends TestCase {
public void testGetAlphanumericCode() {
public void testGetAlphanumericCode() throws WriterException {
// The first ten code points are numbers.
for (int i = 0; i < 10; ++i) {
assertEquals(i, Encoder.GetAlphanumericCode('0' + i));
@ -53,7 +54,7 @@ public final class EncoderTestCase extends TestCase {
assertEquals(-1, Encoder.GetAlphanumericCode('\0'));
}
public void testChooseMode() {
public void testChooseMode() throws WriterException {
// Numeric mode.
assertEquals(QRCode.MODE_NUMERIC, Encoder.ChooseMode(new ByteArray("0")));
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)));
}
public void testEncode() {
public void testEncode() throws WriterException {
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.
String expected =
"<<\n" +
@ -123,85 +124,99 @@ public final class EncoderTestCase extends TestCase {
assertEquals(expected, qr_code.toString());
}
public void testAppendModeInfo() {
public void testAppendModeInfo() throws WriterException {
BitVector bits = new BitVector();
assertTrue(Encoder.AppendModeInfo(QRCode.MODE_NUMERIC, bits));
Encoder.AppendModeInfo(QRCode.MODE_NUMERIC, bits);
assertEquals("0001", bits.toString());
}
public void testAppendLengthInfo() {
public void testAppendLengthInfo() throws WriterException {
{
BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(1, // 1 letter (1/1).
Encoder.AppendLengthInfo(1, // 1 letter (1/1).
1, // version 1.
QRCode.MODE_NUMERIC,
bits));
bits);
assertEquals("0000000001", bits.toString()); // 10 bits.
}
{
BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(2, // 2 letters (2/1).
Encoder.AppendLengthInfo(2, // 2 letters (2/1).
10, // version 10.
QRCode.MODE_ALPHANUMERIC,
bits));
bits);
assertEquals("00000000010", bits.toString()); // 11 bits.
}
{
BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(255, // 255 letter (255/1).
Encoder.AppendLengthInfo(255, // 255 letter (255/1).
27, // version 27.
QRCode.MODE_8BIT_BYTE,
bits));
bits);
assertEquals("0000000011111111", bits.toString()); // 16 bits.
}
{
BitVector bits = new BitVector();
assertTrue(Encoder.AppendLengthInfo(1024, // 512 letters (1024/2).
Encoder.AppendLengthInfo(1024, // 512 letters (1024/2).
40, // version 40.
QRCode.MODE_KANJI,
bits));
bits);
assertEquals("001000000000", bits.toString()); // 12 bits.
}
}
public void testAppendBytes() {
public void testAppendBytes() throws WriterException {
{
// Should use AppendNumericBytes.
// 1 = 01 = 0001 in 4 bits.
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());
// '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.
// A = 10 = 0xa = 001010 in 6 bits
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());
// 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.
// 0x61, 0x62, 0x63
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());
// Anything can be encoded in QRCode.MODE_8BIT_BYTE.
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.
// 0x93, 0x5f
BitVector bits = new BitVector();
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());
// 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.
}
public void testTerminateBits() {
public void testTerminateBits() throws WriterException {
{
BitVector v = new BitVector();
assertTrue(Encoder.TerminateBits(0, v));
Encoder.TerminateBits(0, v);
assertEquals("", v.toString());
}
{
BitVector v = new BitVector();
assertTrue(Encoder.TerminateBits(1, v));
Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString());
}
{
BitVector v = new BitVector();
v.AppendBits(0, 3); // Append 000
assertTrue(Encoder.TerminateBits(1, v));
Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString());
}
{
BitVector v = new BitVector();
v.AppendBits(0, 5); // Append 00000
assertTrue(Encoder.TerminateBits(1, v));
Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString());
}
{
BitVector v = new BitVector();
v.AppendBits(0, 8); // Append 00000000
assertTrue(Encoder.TerminateBits(1, v));
Encoder.TerminateBits(1, v);
assertEquals("00000000", v.toString());
}
{
BitVector v = new BitVector();
assertTrue(Encoder.TerminateBits(2, v));
Encoder.TerminateBits(2, v);
assertEquals("0000000011101100", v.toString());
}
{
BitVector v = new BitVector();
v.AppendBits(0, 1); // Append 0
assertTrue(Encoder.TerminateBits(3, v));
Encoder.TerminateBits(3, v);
assertEquals("000000001110110000010001", v.toString());
}
}
public void testGetNumDataBytesAndNumECBytesForBlockID() {
public void testGetNumDataBytesAndNumECBytesForBlockID() throws WriterException {
int[] num_data_bytes = new int[1];
int[] num_ec_bytes = new int[1];
// Version 1-H.
@ -287,7 +302,7 @@ public final class EncoderTestCase extends TestCase {
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};
BitVector in = new BitVector();
@ -295,7 +310,7 @@ public final class EncoderTestCase extends TestCase {
in.AppendBits(data_byte, 8);
}
BitVector out = new BitVector();
assertTrue(Encoder.InterleaveWithECBytes(in, 26, 9, 1, out));
Encoder.InterleaveWithECBytes(in, 26, 9, 1, out);
final byte[] expected = {
// Data bytes.
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);
}
BitVector out = new BitVector();
assertTrue(Encoder.InterleaveWithECBytes(in, 134, 62, 4, out));
Encoder.InterleaveWithECBytes(in, 134, 62, 4, out);
final byte[] expected = {
// Data bytes.
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.
BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("1"), bits));
Encoder.AppendNumericBytes(new ByteArray("1"), bits);
assertEquals("0001" , bits.toString());
}
{
// 12 = 0xc = 0001100 in 7 bits.
BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("12"), bits));
Encoder.AppendNumericBytes(new ByteArray("12"), bits);
assertEquals("0001100" , bits.toString());
}
{
// 123 = 0x7b = 0001111011 in 10 bits.
BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("123"), bits));
Encoder.AppendNumericBytes(new ByteArray("123"), bits);
assertEquals("0001111011" , bits.toString());
}
{
// 1234 = "123" + "4" = 0001111011 + 0100
BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray("1234"), bits));
Encoder.AppendNumericBytes(new ByteArray("1234"), bits);
assertEquals("0001111011" + "0100" , bits.toString());
}
{
// Empty.
BitVector bits = new BitVector();
assertTrue(Encoder.AppendNumericBytes(new ByteArray(""), bits));
Encoder.AppendNumericBytes(new ByteArray(""), bits);
assertEquals("" , bits.toString());
}
{
// Invalid data.
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
BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray("A"), bits));
Encoder.AppendAlphanumericBytes(new ByteArray("A"), bits);
assertEquals("001010" , bits.toString());
}
{
// AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits
BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray("AB"), bits));
Encoder.AppendAlphanumericBytes(new ByteArray("AB"), bits);
assertEquals("00111001101", bits.toString());
}
{
// ABC = "AB" + "C" = 00111001101 + 001100
BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray("ABC"), bits));
Encoder.AppendAlphanumericBytes(new ByteArray("ABC"), bits);
assertEquals("00111001101" + "001100" , bits.toString());
}
{
// Empty.
BitVector bits = new BitVector();
assertTrue(Encoder.AppendAlphanumericBytes(new ByteArray(""), bits));
Encoder.AppendAlphanumericBytes(new ByteArray(""), bits);
assertEquals("" , bits.toString());
}
{
// Invalid data.
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
BitVector bits = new BitVector();
assertTrue(Encoder.Append8BitBytes(new ByteArray("abc"), bits));
Encoder.Append8BitBytes(new ByteArray("abc"), bits);
assertEquals("01100001" + "01100010" + "01100011", bits.toString());
}
{
// Empty.
BitVector bits = new BitVector();
assertTrue(Encoder.Append8BitBytes(new ByteArray(""), bits));
Encoder.Append8BitBytes(new ByteArray(""), bits);
assertEquals("", bits.toString());
}
}
// Numbers are from page 21 of JISX0510:2004
public void testAppendKanjiBytes() {
public void testAppendKanjiBytes() throws WriterException {
{
BitVector bits = new BitVector();
byte[] dat1 = {(byte)0x93,0x5f};
assertTrue(Encoder.AppendKanjiBytes(new ByteArray(dat1), bits));
Encoder.AppendKanjiBytes(new ByteArray(dat1), bits);
assertEquals("0110110011111", bits.toString());
byte[] dat2 = {(byte)0xe4,(byte)0xaa};
assertTrue(Encoder.AppendKanjiBytes(new ByteArray(dat2), bits));
Encoder.AppendKanjiBytes(new ByteArray(dat2), bits);
assertEquals("0110110011111" + "1101010101010", bits.toString());
}
}

View file

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

View file

@ -17,6 +17,7 @@
package com.google.zxing.qrcode.encoder;
import com.google.zxing.common.ByteMatrix;
import com.google.zxing.WriterException;
import junit.framework.TestCase;
/**
@ -208,19 +209,29 @@ public final class QRCodeTestCase extends TestCase {
assertEquals("UNKNOWN", QRCode.ECLevelToString(QRCode.NUM_EC_LEVELS));
}
public void testGetModeCode() {
public void testGetModeCode() throws WriterException {
assertEquals(1, QRCode.GetModeCode(QRCode.MODE_NUMERIC));
assertEquals(2, QRCode.GetModeCode(QRCode.MODE_ALPHANUMERIC));
assertEquals(4, QRCode.GetModeCode(QRCode.MODE_8BIT_BYTE));
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(0, QRCode.GetECLevelCode(QRCode.EC_LEVEL_M));
assertEquals(3, QRCode.GetECLevelCode(QRCode.EC_LEVEL_Q));
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"));
// }
}