Added code for the Data Matrix decoder.
Added initial ASCII tests for Data Matrix. Added test symbols for DataMatrix. Modified MultiFormatReader to try the DataMatrixReader after 1D and QRCode. git-svn-id: https://zxing.googlecode.com/svn/trunk@264 59b500cc-1b3d-0410-9834-0bbf25fbcc57
|
@ -18,6 +18,7 @@ package com.google.zxing;
|
||||||
|
|
||||||
import com.google.zxing.oned.MultiFormatOneDReader;
|
import com.google.zxing.oned.MultiFormatOneDReader;
|
||||||
import com.google.zxing.qrcode.QRCodeReader;
|
import com.google.zxing.qrcode.QRCodeReader;
|
||||||
|
import com.google.zxing.datamatrix.DataMatrixReader;
|
||||||
|
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
@ -51,10 +52,14 @@ public final class MultiFormatReader implements Reader {
|
||||||
if (possibleFormats.contains(BarcodeFormat.QR_CODE)) {
|
if (possibleFormats.contains(BarcodeFormat.QR_CODE)) {
|
||||||
readers.addElement(new QRCodeReader());
|
readers.addElement(new QRCodeReader());
|
||||||
}
|
}
|
||||||
|
if (possibleFormats.contains(BarcodeFormat.DATAMATRIX)) {
|
||||||
|
readers.addElement(new DataMatrixReader());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (readers.isEmpty()) {
|
if (readers.isEmpty()) {
|
||||||
readers.addElement(new MultiFormatOneDReader());
|
readers.addElement(new MultiFormatOneDReader());
|
||||||
readers.addElement(new QRCodeReader());
|
readers.addElement(new QRCodeReader());
|
||||||
|
readers.addElement(new DataMatrixReader());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < readers.size(); i++) {
|
for (int i = 0; i < readers.size(); i++) {
|
||||||
|
|
124
core/src/com/google/zxing/datamatrix/DataMatrixReader.java
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.Reader;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.datamatrix.decoder.Decoder;
|
||||||
|
import com.google.zxing.datamatrix.detector.Detector;
|
||||||
|
import com.google.zxing.datamatrix.detector.DetectorResult;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation can detect and decode Data Matrix codes in an image.
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class DataMatrixReader implements Reader {
|
||||||
|
|
||||||
|
private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||||
|
|
||||||
|
private final Decoder decoder = new Decoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates and decodes a Data Matrix code in an image.
|
||||||
|
*
|
||||||
|
* @return a String representing the content encoded by the Data Matrix code
|
||||||
|
* @throws ReaderException if a Data Matrix code cannot be found, or cannot be decoded
|
||||||
|
*/
|
||||||
|
public Result decode(MonochromeBitmapSource image) throws ReaderException {
|
||||||
|
return decode(image, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result decode(MonochromeBitmapSource image, Hashtable hints)
|
||||||
|
throws ReaderException {
|
||||||
|
String text;
|
||||||
|
ResultPoint[] points;
|
||||||
|
//if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
|
||||||
|
BitMatrix bits = extractPureBits(image);
|
||||||
|
text = decoder.decode(bits);
|
||||||
|
points = NO_POINTS;
|
||||||
|
//} else {
|
||||||
|
// DetectorResult result = new Detector(image).detect();
|
||||||
|
// text = decoder.decode(result.getBits());
|
||||||
|
// points = result.getPoints();
|
||||||
|
//}
|
||||||
|
return new Result(text, points, BarcodeFormat.DATAMATRIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image
|
||||||
|
* which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border
|
||||||
|
* around it. This is a specialized method that works exceptionally fast in this special
|
||||||
|
* case.
|
||||||
|
*/
|
||||||
|
private static BitMatrix extractPureBits(MonochromeBitmapSource image)
|
||||||
|
throws ReaderException {
|
||||||
|
// Now need to determine module size in pixels
|
||||||
|
|
||||||
|
// First, skip white border by tracking diagonally from the top left down and to the right:
|
||||||
|
int borderWidth = 0;
|
||||||
|
while (!image.isBlack(borderWidth, borderWidth)) {
|
||||||
|
borderWidth++;
|
||||||
|
}
|
||||||
|
// And then keep tracking across the top-left black module to determine module size
|
||||||
|
int moduleEnd = borderWidth + 1;
|
||||||
|
while (image.isBlack(moduleEnd, borderWidth)) {
|
||||||
|
moduleEnd++;
|
||||||
|
}
|
||||||
|
int moduleSize = moduleEnd - borderWidth;
|
||||||
|
|
||||||
|
// And now find where the bottommost black module on the first column ends
|
||||||
|
int columnEndOfSymbol = image.getHeight() - 1;
|
||||||
|
while (!image.isBlack(borderWidth, columnEndOfSymbol)) {
|
||||||
|
columnEndOfSymbol--;
|
||||||
|
}
|
||||||
|
columnEndOfSymbol++;
|
||||||
|
|
||||||
|
// Make sure width of barcode is a multiple of module size
|
||||||
|
if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) {
|
||||||
|
throw new ReaderException("Bad module size / width: " + moduleSize +
|
||||||
|
" / " + (columnEndOfSymbol - borderWidth));
|
||||||
|
}
|
||||||
|
int dimension = (columnEndOfSymbol - borderWidth) / moduleSize;
|
||||||
|
|
||||||
|
// Push in the "border" by half the module width so that we start
|
||||||
|
// sampling in the middle of the module. Just in case the image is a
|
||||||
|
// little off, this will help recover.
|
||||||
|
borderWidth += moduleSize >> 1;
|
||||||
|
|
||||||
|
// Now just read off the bits
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
int iOffset = borderWidth + i * moduleSize;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (image.isBlack(borderWidth + j * moduleSize, iOffset)) {
|
||||||
|
bits.set(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,446 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
final class BitMatrixParser {
|
||||||
|
|
||||||
|
private final BitMatrix mappingBitMatrix;
|
||||||
|
private final BitMatrix readMappingMatrix;
|
||||||
|
private Version version;
|
||||||
|
// private FormatInformation parsedFormatInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bitMatrix {@link BitMatrix} to parse
|
||||||
|
* @throws ReaderException if dimension is < 10 or > 144 or not 0 mod 2
|
||||||
|
*/
|
||||||
|
BitMatrixParser(BitMatrix bitMatrix) throws ReaderException {
|
||||||
|
int dimension = bitMatrix.getDimension();
|
||||||
|
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {
|
||||||
|
throw new ReaderException("Invalid dimension (" + dimension + ") Must be 0 mod 2 and >= 10 and <= 144");
|
||||||
|
}
|
||||||
|
|
||||||
|
version = readVersion(bitMatrix);
|
||||||
|
this.mappingBitMatrix = ExtractDataRegion(bitMatrix, version);
|
||||||
|
// TODO(bbrown): Make this work for rectangular symbols
|
||||||
|
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getDimension());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates the version object based on the dimension of the original bit matrix from
|
||||||
|
* the datamatrix code.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
|
||||||
|
*
|
||||||
|
* @param bitMatrix Original {@link BitMatrix} including alignment patterns
|
||||||
|
* @return {@link Version} encapsulating the Data Matrix Code's "version"
|
||||||
|
* @throws ReaderException if the dimensions of the mapping matrix are not valid
|
||||||
|
* Data Matrix dimensions.
|
||||||
|
*/
|
||||||
|
Version readVersion(BitMatrix bitMatrix) throws ReaderException {
|
||||||
|
|
||||||
|
if (version != null) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bbrown): make this work for rectangular dimensions as well.
|
||||||
|
int numRows = bitMatrix.getDimension();
|
||||||
|
int numColumns = numRows;
|
||||||
|
|
||||||
|
return Version.getVersionForDimensions(numRows, numColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns)
|
||||||
|
* in the correct order in order to reconstitute the codewords bytes contained within the
|
||||||
|
* Data Matrix Code.</p>
|
||||||
|
*
|
||||||
|
* @return bytes encoded within the Data Matrix Code
|
||||||
|
* @throws ReaderException if the exact number of bytes expected is not read
|
||||||
|
*/
|
||||||
|
byte[] readCodewords() throws ReaderException {
|
||||||
|
|
||||||
|
byte[] result = new byte[version.getTotalCodewords()];
|
||||||
|
int resultOffset = 0;
|
||||||
|
int currentByte = 0;
|
||||||
|
int bitsRead = 0;
|
||||||
|
|
||||||
|
int row = 4;
|
||||||
|
int column = 0;
|
||||||
|
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
|
||||||
|
int numRows = mappingBitMatrix.getDimension();
|
||||||
|
int numColumns = numRows;
|
||||||
|
|
||||||
|
boolean corner1Read = false;
|
||||||
|
boolean corner2Read = false;
|
||||||
|
boolean corner3Read = false;
|
||||||
|
boolean corner4Read = false;
|
||||||
|
|
||||||
|
// Read all of the codewords
|
||||||
|
do {
|
||||||
|
// Check the four corner cases
|
||||||
|
if ((row == numRows) && (column == 0) && !corner1Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner1(numRows, numColumns);
|
||||||
|
row -= 2; column +=2;
|
||||||
|
corner1Read = true;
|
||||||
|
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner2(numRows, numColumns);
|
||||||
|
row -= 2; column +=2;
|
||||||
|
corner2Read = true;
|
||||||
|
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner3(numRows, numColumns);
|
||||||
|
row -= 2; column +=2;
|
||||||
|
corner3Read = true;
|
||||||
|
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner4(numRows, numColumns);
|
||||||
|
row -= 2; column +=2;
|
||||||
|
corner4Read = true;
|
||||||
|
} else {
|
||||||
|
// Sweep upward diagonally to the right
|
||||||
|
do {
|
||||||
|
if ((row < numRows) && (column >= 0) && !readMappingMatrix.get(row, column)) {
|
||||||
|
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
|
||||||
|
}
|
||||||
|
row -= 2; column +=2;
|
||||||
|
} while ((row >= 0) && (column < numColumns));
|
||||||
|
row += 1; column +=3;
|
||||||
|
|
||||||
|
// Sweep downward giagonally to the left
|
||||||
|
do {
|
||||||
|
if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(row, column)) {
|
||||||
|
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
|
||||||
|
}
|
||||||
|
row += 2; column -=2;
|
||||||
|
} while ((row < numRows) && (column >= 0));
|
||||||
|
row += 3; column +=1;
|
||||||
|
}
|
||||||
|
} while ((row < numRows) || (column < numColumns));
|
||||||
|
|
||||||
|
if (resultOffset != version.getTotalCodewords()) {
|
||||||
|
throw new ReaderException("Did not read all codewords");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads a bit of the mapping matrix accounting for boundry wrapping.</p>
|
||||||
|
*
|
||||||
|
* @param Row to read in the mapping matrix
|
||||||
|
* @param Column to read in the mapping matrix
|
||||||
|
* @param Number of rows in the mapping matrix
|
||||||
|
* @param Number of columns in the mapping matrix
|
||||||
|
* @return value of the given bit in the mapping matrix
|
||||||
|
*/
|
||||||
|
boolean readModule(int row, int column, int numRows, int numColumns) {
|
||||||
|
// Adjust the row and column indicies based on boundry wrapping
|
||||||
|
if (row < 0) {
|
||||||
|
row += numRows;
|
||||||
|
column += 4 - ((numRows + 4) & 0x07);
|
||||||
|
}
|
||||||
|
if (column < 0) {
|
||||||
|
column += numColumns;
|
||||||
|
row += 4 - ((numColumns + 4) & 0x07);
|
||||||
|
}
|
||||||
|
readMappingMatrix.set(row, column);
|
||||||
|
return mappingBitMatrix.get(row, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the standard utah shaped pattern.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
|
||||||
|
*
|
||||||
|
* @param Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
|
||||||
|
* @param Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
|
||||||
|
* @param Number of rows in the mapping matrix
|
||||||
|
* @param Number of columns in the mapping matrix
|
||||||
|
* @return byte from the utah shape
|
||||||
|
*/
|
||||||
|
int readUtah(int row, int column, int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(row - 2, column - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 2, column - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 1, column - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 1, column - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 1, column, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row, column - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row, column - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row, column, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 1.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.3</p>
|
||||||
|
*
|
||||||
|
* @param Number of rows in the mapping matrix
|
||||||
|
* @param Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 1
|
||||||
|
*/
|
||||||
|
int readCorner1(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 2.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.4</p>
|
||||||
|
*
|
||||||
|
* @param Number of rows in the mapping matrix
|
||||||
|
* @param Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 2
|
||||||
|
*/
|
||||||
|
int readCorner2(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 4, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 3.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.5</p>
|
||||||
|
*
|
||||||
|
* @param Number of rows in the mapping matrix
|
||||||
|
* @param Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 3
|
||||||
|
*/
|
||||||
|
int readCorner3(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 3, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 4.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.6</p>
|
||||||
|
*
|
||||||
|
* @param Number of rows in the mapping matrix
|
||||||
|
* @param Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 4
|
||||||
|
*/
|
||||||
|
int readCorner4(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Extracts the data region from a {@link BitMatrix} that contains
|
||||||
|
* alignment patterns.</p>
|
||||||
|
*
|
||||||
|
* @param bitMarix Original {@link BitMatrix} with alignment patterns
|
||||||
|
* @param version {@link Version} information corresponding with the bitMatrix
|
||||||
|
* @return BitMatrix that has the alignment patterns removed
|
||||||
|
*/
|
||||||
|
BitMatrix ExtractDataRegion(BitMatrix bitMatrix, Version version) {
|
||||||
|
int symbolSizeRows = version.getSymbolSizeRows();
|
||||||
|
int symbolSizeColumns = version.getSymbolSizeColumns();
|
||||||
|
|
||||||
|
// TODO(bbrown): Make this work with rectangular codes
|
||||||
|
if (bitMatrix.getDimension() != symbolSizeRows) {
|
||||||
|
throw new IllegalArgumentException("Dimension of bitMarix must match the version size");
|
||||||
|
}
|
||||||
|
|
||||||
|
int dataRegionSizeRows = version.getDataRegionSizeRows();
|
||||||
|
int dataRegionSizeColumns = version.getDataRegionSizeColumns();
|
||||||
|
|
||||||
|
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
|
||||||
|
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
|
||||||
|
|
||||||
|
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
|
||||||
|
int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
|
||||||
|
|
||||||
|
// TODO(bbrown): Make this work with rectangular codes
|
||||||
|
BitMatrix mappingBitMatrix = new BitMatrix(sizeDataRegionRow);
|
||||||
|
int readRowOffset = 0;
|
||||||
|
int readColumnOffset = 0;
|
||||||
|
int writeRowOffset = 0;
|
||||||
|
int writeColumnOffset = 0;
|
||||||
|
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
|
||||||
|
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
|
||||||
|
for (int i = 0; i < dataRegionSizeRows; ++i) {
|
||||||
|
for (int j = 0; j < dataRegionSizeColumns; ++j) {
|
||||||
|
readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
|
||||||
|
readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
|
||||||
|
writeRowOffset = dataRegionRow * dataRegionSizeRows + i;
|
||||||
|
writeColumnOffset = dataRegionColumn * dataRegionSizeColumns + j;
|
||||||
|
|
||||||
|
if (bitMatrix.get(readRowOffset, readColumnOffset)) {
|
||||||
|
mappingBitMatrix.set(writeRowOffset, writeColumnOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappingBitMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
118
core/src/com/google/zxing/datamatrix/decoder/DataBlock.java
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.decoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
|
||||||
|
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||||
|
* is represented by an instance of this class.</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
final class DataBlock {
|
||||||
|
|
||||||
|
private final int numDataCodewords;
|
||||||
|
private final byte[] codewords;
|
||||||
|
|
||||||
|
private DataBlock(int numDataCodewords, byte[] codewords) {
|
||||||
|
this.numDataCodewords = numDataCodewords;
|
||||||
|
this.codewords = codewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.
|
||||||
|
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||||
|
* method will separate the data into original blocks.</p>
|
||||||
|
*
|
||||||
|
* @param rawCodewords bytes as read directly from the Data Matrix Code
|
||||||
|
* @param version version of the Data Matrix Code
|
||||||
|
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
|
||||||
|
* Data Matrix Code
|
||||||
|
*/
|
||||||
|
static DataBlock[] getDataBlocks(byte[] rawCodewords,
|
||||||
|
Version version) {
|
||||||
|
// Figure out the number and size of data blocks used by this version
|
||||||
|
Version.ECBlocks ecBlocks = version.getECBlocks();
|
||||||
|
|
||||||
|
// First count the total number of data blocks
|
||||||
|
int totalBlocks = 0;
|
||||||
|
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||||
|
for (int i = 0; i < ecBlockArray.length; i++) {
|
||||||
|
totalBlocks += ecBlockArray[i].getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||||
|
DataBlock[] result = new DataBlock[totalBlocks];
|
||||||
|
int numResultBlocks = 0;
|
||||||
|
for (int j = 0; j < ecBlockArray.length; j++) {
|
||||||
|
Version.ECB ecBlock = ecBlockArray[j];
|
||||||
|
for (int i = 0; i < ecBlock.getCount(); i++) {
|
||||||
|
int numDataCodewords = ecBlock.getDataCodewords();
|
||||||
|
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
|
||||||
|
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All blocks have the same amount of data, except that the last n
|
||||||
|
// (where n may be 0) have 1 less byte. Figure out where these start.
|
||||||
|
// TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144
|
||||||
|
int longerBlocksTotalCodewords = result[0].codewords.length;
|
||||||
|
int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;
|
||||||
|
|
||||||
|
int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords();
|
||||||
|
int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1;
|
||||||
|
// The last elements of result may be 1 element shorter for 144 matrix
|
||||||
|
// first fill out as many elements as all of them have minus 1
|
||||||
|
int rawCodewordsOffset = 0;
|
||||||
|
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
|
||||||
|
for (int j = 0; j < numResultBlocks; j++) {
|
||||||
|
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill out the last data block in the longer ones
|
||||||
|
boolean specialVersion = version.getVersionNumber() == 24;
|
||||||
|
int numLongerBlocks = specialVersion ? 8 : numResultBlocks;
|
||||||
|
for (int j = 0; j < numLongerBlocks; j++) {
|
||||||
|
result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now add in error correction blocks
|
||||||
|
int max = result[0].codewords.length;
|
||||||
|
for (int i = longerBlocksNumDataCodewords; i < max; i++) {
|
||||||
|
for (int j = 0; j < numResultBlocks; j++) {
|
||||||
|
int iOffset = (specialVersion && j > 7) ? i - 1 : i;
|
||||||
|
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawCodewordsOffset != rawCodewords.length) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNumDataCodewords() {
|
||||||
|
return numDataCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getCodewords() {
|
||||||
|
return codewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,462 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitSource;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||||
|
* in one Data Matrix Code. This class decodes the bits back into text.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
final class DecodedBitStreamParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, Annex C Table C.1
|
||||||
|
* The C40 Basic Character Set (*'s used for placeholders for the shift values)
|
||||||
|
*/
|
||||||
|
private static final char[] C40_BASIC_SET_CHARS = new char[]{
|
||||||
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final char[] C40_SHIFT2_SET_CHARS = new char[]{
|
||||||
|
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
|
||||||
|
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, Annex C Table C.2
|
||||||
|
* The Text Basic Character Set (*'s used for placeholders for the shift values)
|
||||||
|
*/
|
||||||
|
private static final char[] TEXT_BASIC_SET_CHARS = new char[]{
|
||||||
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||||
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final char[] TEXT_SHIFT3_SET_CHARS = new char[]{
|
||||||
|
'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127
|
||||||
|
};
|
||||||
|
|
||||||
|
static final int PAD_ENCODE = 0; // Not really an encoding
|
||||||
|
static final int ASCII_ENCODE = 1;
|
||||||
|
static final int C40_ENCODE = 2;
|
||||||
|
static final int TEXT_ENCODE = 3;
|
||||||
|
static final int ANSIX12_ENCODE = 4;
|
||||||
|
static final int EDIFACT_ENCODE = 5;
|
||||||
|
static final int BASE256_ENCODE = 6;
|
||||||
|
|
||||||
|
private DecodedBitStreamParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static String decode(byte[] bytes) throws ReaderException {
|
||||||
|
BitSource bits = new BitSource(bytes);
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
|
||||||
|
int mode = ASCII_ENCODE;
|
||||||
|
do {
|
||||||
|
if (mode != PAD_ENCODE) {
|
||||||
|
if (mode == ASCII_ENCODE) {
|
||||||
|
mode = decodeAsciiSegment(bits, result);
|
||||||
|
} else if (mode == C40_ENCODE) {
|
||||||
|
mode = decodeC40Segment(bits, result);
|
||||||
|
} else if (mode == TEXT_ENCODE) {
|
||||||
|
mode = decodeTextSegment(bits, result);
|
||||||
|
} else if (mode == ANSIX12_ENCODE) {
|
||||||
|
mode = decodeAnsiX12Segment(bits, result);
|
||||||
|
} else if (mode == EDIFACT_ENCODE) {
|
||||||
|
mode = decodeEdifactSegment(bits, result);
|
||||||
|
} else if (mode == BASE256_ENCODE) {
|
||||||
|
mode = decodeBase256Segment(bits, result);
|
||||||
|
} else {
|
||||||
|
throw new ReaderException("Unsupported mode indicator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (mode != PAD_ENCODE && bits.available() > 0);
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
|
||||||
|
*/
|
||||||
|
private static int decodeAsciiSegment(BitSource bits,
|
||||||
|
StringBuffer result) throws ReaderException {
|
||||||
|
char oneByte;
|
||||||
|
boolean upperShift = false;
|
||||||
|
int bytesProcessed = 0;
|
||||||
|
do {
|
||||||
|
oneByte = (char) bits.readBits(8);
|
||||||
|
if (oneByte == 0) {
|
||||||
|
// TODO(bbrown): I think this would be a bug, not sure
|
||||||
|
throw new ReaderException("0 is an invalid ASCII codeword");
|
||||||
|
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
|
||||||
|
oneByte = upperShift ? (char) (oneByte + 128) : oneByte;
|
||||||
|
upperShift = false;
|
||||||
|
result.append((char)(oneByte - 1));
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
} else if (oneByte == 129) { // Pad
|
||||||
|
return PAD_ENCODE;
|
||||||
|
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
|
||||||
|
// TODO(bbrown): Iassume there is some easier way to do this:
|
||||||
|
if (oneByte - 130 < 10) {
|
||||||
|
result.append('0');
|
||||||
|
}
|
||||||
|
result.append(Integer.toString(oneByte - 130));
|
||||||
|
} else if (oneByte == 230) { // Latch to C40 encodation
|
||||||
|
return C40_ENCODE;
|
||||||
|
} else if (oneByte == 231) { // Latch to Base 256 encodation
|
||||||
|
return BASE256_ENCODE;
|
||||||
|
} else if (oneByte == 232) { // FNC1
|
||||||
|
throw new ReaderException("Currently not supporting FNC1");
|
||||||
|
} else if (oneByte == 233) { // Structured Append
|
||||||
|
throw new ReaderException("Currently not supporting Structured Append");
|
||||||
|
} else if (oneByte == 234) { // Reader Programming
|
||||||
|
throw new ReaderException("Currently not supporting Reader Programming");
|
||||||
|
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
|
||||||
|
upperShift = true;
|
||||||
|
} else if (oneByte == 236) { // 05 Macro
|
||||||
|
throw new ReaderException("Currently not supporting 05 Macro");
|
||||||
|
} else if (oneByte == 237) { // 06 Macro
|
||||||
|
throw new ReaderException("Currently not supporting 06 Macro");
|
||||||
|
} else if (oneByte == 238) { // Latch to ANSI X12 encodation
|
||||||
|
return ANSIX12_ENCODE;
|
||||||
|
} else if (oneByte == 239) { // Latch to Text encodation
|
||||||
|
return TEXT_ENCODE;
|
||||||
|
} else if (oneByte == 240) { // Latch to EDIFACT encodation
|
||||||
|
return EDIFACT_ENCODE;
|
||||||
|
} else if (oneByte == 241) { // ECI Character
|
||||||
|
// TODO(bbrown): I think we need to support ECI
|
||||||
|
throw new ReaderException("Currently not supporting ECI Character");
|
||||||
|
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
|
||||||
|
throw new ReaderException(Integer.toString(oneByte) + " should not be used in ASCII encodation");
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
|
||||||
|
*/
|
||||||
|
private static int decodeC40Segment(BitSource bits,
|
||||||
|
StringBuffer result) throws ReaderException {
|
||||||
|
// Three C40 values are encoded in a 16-bit value as
|
||||||
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
char firstByte;
|
||||||
|
int shift = 0;
|
||||||
|
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
|
||||||
|
boolean upperShift = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
|
if (bits.available() == 8) {
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstByte = (char) bits.readBits(8);
|
||||||
|
|
||||||
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fullBitValue = firstByte * 256 + bits.readBits(8) - 1;
|
||||||
|
|
||||||
|
char[] CValues = new char[3];
|
||||||
|
CValues[0] = (char) (fullBitValue / 1600);
|
||||||
|
fullBitValue -= CValues[0] * 1600;
|
||||||
|
CValues[1] = (char) (fullBitValue / 40);
|
||||||
|
fullBitValue -= CValues[1] * 40;
|
||||||
|
CValues[2] = (char) (fullBitValue);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (shift == 0) {
|
||||||
|
if (CValues[i] == 0) { // Shift 1
|
||||||
|
shift = 1;
|
||||||
|
continue;
|
||||||
|
} else if (CValues[i] == 1) { // Shift 2
|
||||||
|
shift = 2;
|
||||||
|
continue;
|
||||||
|
} else if (CValues[i] == 2) { // Shift 3
|
||||||
|
shift = 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char)(C40_BASIC_SET_CHARS[CValues[i]] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(C40_BASIC_SET_CHARS[CValues[i]]);
|
||||||
|
}
|
||||||
|
} else if (shift == 1) {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (CValues[i] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append((char) CValues[i]);
|
||||||
|
}
|
||||||
|
} else if (shift == 2) {
|
||||||
|
if (CValues[i] < 27) {
|
||||||
|
if(upperShift) {
|
||||||
|
result.append((char)(C40_SHIFT2_SET_CHARS[CValues[i]] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(C40_SHIFT2_SET_CHARS[CValues[i]]);
|
||||||
|
}
|
||||||
|
} else if (CValues[i] == 27) { // FNC1
|
||||||
|
throw new ReaderException("Currently not supporting FNC1");
|
||||||
|
} else if (CValues[i] == 30) { // Upper Shirt
|
||||||
|
upperShift = true;
|
||||||
|
} else {
|
||||||
|
throw new ReaderException(Integer.toString(CValues[i]) + " is not valid in the C40 Shift 2 set");
|
||||||
|
}
|
||||||
|
} else if (shift == 3) {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (CValues[i] + 224));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append((char) CValues[i] + 96);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ReaderException("Invalid shift value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
|
||||||
|
*/
|
||||||
|
private static int decodeTextSegment(BitSource bits,
|
||||||
|
StringBuffer result) throws ReaderException {
|
||||||
|
// Three Text values are encoded in a 16-bit value as
|
||||||
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
char firstByte;
|
||||||
|
int shift = 0;
|
||||||
|
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
|
||||||
|
boolean upperShift = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
|
if (bits.available() == 8) {
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstByte = (char) bits.readBits(8);
|
||||||
|
|
||||||
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fullBitValue = firstByte * 256 + bits.readBits(8) - 1;
|
||||||
|
|
||||||
|
char[] CValues = new char[3];
|
||||||
|
CValues[0] = (char) (fullBitValue / 1600);
|
||||||
|
fullBitValue -= CValues[0] * 1600;
|
||||||
|
CValues[1] = (char) (fullBitValue / 40);
|
||||||
|
fullBitValue -= CValues[1] * 40;
|
||||||
|
CValues[2] = (char) (fullBitValue);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (shift == 0) {
|
||||||
|
if (CValues[i] == 0) { // Shift 1
|
||||||
|
shift = 1;
|
||||||
|
continue;
|
||||||
|
} else if (CValues[i] == 1) { // Shift 2
|
||||||
|
shift = 2;
|
||||||
|
continue;
|
||||||
|
} else if (CValues[i] == 2) { // Shift 3
|
||||||
|
shift = 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char)(TEXT_BASIC_SET_CHARS[CValues[i]] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(TEXT_BASIC_SET_CHARS[CValues[i]]);
|
||||||
|
}
|
||||||
|
} else if (shift == 1) {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (CValues[i] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append((char) CValues[i]);
|
||||||
|
}
|
||||||
|
} else if (shift == 2) {
|
||||||
|
// Shift 2 for Text is the same encoding as C40
|
||||||
|
if (CValues[i] < 27) {
|
||||||
|
if(upperShift) {
|
||||||
|
result.append((char)(C40_SHIFT2_SET_CHARS[CValues[i]] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(C40_SHIFT2_SET_CHARS[CValues[i]]);
|
||||||
|
}
|
||||||
|
} else if (CValues[i] == 27) { // FNC1
|
||||||
|
throw new ReaderException("Currently not supporting FNC1");
|
||||||
|
} else if (CValues[i] == 30) { // Upper Shirt
|
||||||
|
upperShift = true;
|
||||||
|
} else {
|
||||||
|
throw new ReaderException(Integer.toString(CValues[i]) + " is not valid in the C40 Shift 2 set");
|
||||||
|
}
|
||||||
|
} else if (shift == 3) {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char)(TEXT_SHIFT3_SET_CHARS[CValues[i]] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(TEXT_SHIFT3_SET_CHARS[CValues[i]]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ReaderException("Invalid shift value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.7
|
||||||
|
*/
|
||||||
|
private static int decodeAnsiX12Segment(BitSource bits,
|
||||||
|
StringBuffer result) throws ReaderException {
|
||||||
|
// Three ANSI X12 values are encoded in a 16-bit value as
|
||||||
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
char firstByte;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
|
if (bits.available() == 8) {
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstByte = (char) bits.readBits(8);
|
||||||
|
|
||||||
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fullBitValue = firstByte * 256 + bits.readBits(8) - 1;
|
||||||
|
|
||||||
|
char[] CValues = new char[3];
|
||||||
|
CValues[0] = (char) (fullBitValue / 1600);
|
||||||
|
fullBitValue -= CValues[0] * 1600;
|
||||||
|
CValues[1] = (char) (fullBitValue / 40);
|
||||||
|
fullBitValue -= CValues[1] * 40;
|
||||||
|
CValues[2] = (char) (fullBitValue);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
// TODO(bbrown): These really aren't X12 symbols, we are converting to ASCII chars
|
||||||
|
if (CValues[i] == 0) { // X12 segment terminator <CR>
|
||||||
|
result.append("<CR>");
|
||||||
|
} else if (CValues[i] == 1) { // X12 segment separator *
|
||||||
|
result.append('*');
|
||||||
|
} else if (CValues[i] == 2) { // X12 sub-element separator >
|
||||||
|
result.append('>');
|
||||||
|
} else if (CValues[i] == 3) { // space
|
||||||
|
result.append(' ');
|
||||||
|
} else if (CValues[i] < 14) { // 0 - 9
|
||||||
|
result.append((char) (CValues[i] + 44));
|
||||||
|
} else if (CValues[i] < 40) { // A - Z
|
||||||
|
result.append((char) (CValues[i] + 51));
|
||||||
|
} else {
|
||||||
|
throw new ReaderException(Integer.toString(CValues[i]) + " is not valid in the ANSI X12 set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3
|
||||||
|
*/
|
||||||
|
private static int decodeEdifactSegment(BitSource bits,
|
||||||
|
StringBuffer result) throws ReaderException {
|
||||||
|
boolean unlatch = false;
|
||||||
|
do {
|
||||||
|
// If there is only two or less bytes left then it will be encoded as ASCII
|
||||||
|
if (bits.available() <= 16) {
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char edifactValue;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
edifactValue = (char) bits.readBits(6);
|
||||||
|
|
||||||
|
// Check for the unlatch character
|
||||||
|
if (edifactValue == 0x2B67) { // 011111
|
||||||
|
unlatch = true;
|
||||||
|
// If we encounter the unlatch code then continue reading because the Codeword triple
|
||||||
|
// is padded with 0's
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unlatch) {
|
||||||
|
if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit
|
||||||
|
edifactValue |= 64; // Add a leading 01 to the 6 bit binary value
|
||||||
|
}
|
||||||
|
result.append((char) edifactValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!unlatch && bits.available() > 0);
|
||||||
|
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
|
||||||
|
*/
|
||||||
|
private static int decodeBase256Segment(BitSource bits,
|
||||||
|
StringBuffer result) throws ReaderException {
|
||||||
|
// Figure out how long the Base 256 Segment is.
|
||||||
|
char d1 = (char) bits.readBits(8);
|
||||||
|
int count = 0;
|
||||||
|
if (d1 == 0) { // Read the remainder of the symbol
|
||||||
|
count = bits.available() / 8;
|
||||||
|
} else if (d1 < 250) {
|
||||||
|
count = d1;
|
||||||
|
} else {
|
||||||
|
count = 250 * (d1 - 249) + bits.readBits(8);
|
||||||
|
}
|
||||||
|
char[] readBytes = new char[count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
result.append((char)unrandomize255State((char) bits.readBits(8), count));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, Annex B, B.2
|
||||||
|
*/
|
||||||
|
private static char unrandomize255State(char randomizedBase256Codeword,
|
||||||
|
int base256CodewordPosition) {
|
||||||
|
char pseudoRandomNumber = (char) (((149 * base256CodewordPosition) % 255) + 1);
|
||||||
|
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
|
||||||
|
if (tempVariable >= 0) {
|
||||||
|
return (char) tempVariable;
|
||||||
|
} else {
|
||||||
|
return (char) (tempVariable + 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
130
core/src/com/google/zxing/datamatrix/decoder/Decoder.java
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.common.reedsolomon.GF256;
|
||||||
|
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||||
|
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
|
||||||
|
* the Data Matrix Code from an image.</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class Decoder {
|
||||||
|
|
||||||
|
private final ReedSolomonDecoder rsDecoder;
|
||||||
|
|
||||||
|
public Decoder() {
|
||||||
|
rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.
|
||||||
|
* "true" is taken to mean a black module.</p>
|
||||||
|
*
|
||||||
|
* @param image booleans representing white/black Data Matrix Code modules
|
||||||
|
* @return text encoded within the Data Matrix Code
|
||||||
|
* @throws ReaderException if the Data Matrix Code cannot be decoded
|
||||||
|
*/
|
||||||
|
public String decode(boolean[][] image) throws ReaderException {
|
||||||
|
int dimension = image.length;
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (image[i][j]) {
|
||||||
|
bits.set(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decode(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken
|
||||||
|
* to mean a black module.</p>
|
||||||
|
*
|
||||||
|
* @param bits booleans representing white/black Data Matrix Code modules
|
||||||
|
* @return text encoded within the Data Matrix Code
|
||||||
|
* @throws ReaderException if the Data Matrix Code cannot be decoded
|
||||||
|
*/
|
||||||
|
public String decode(BitMatrix bits) throws ReaderException {
|
||||||
|
|
||||||
|
// Construct a parser and read version, error-correction level
|
||||||
|
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||||
|
Version version = parser.readVersion(bits);
|
||||||
|
|
||||||
|
// Read codewords
|
||||||
|
byte[] codewords = parser.readCodewords();
|
||||||
|
// Separate into data blocks
|
||||||
|
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version);
|
||||||
|
|
||||||
|
// Count total number of data bytes
|
||||||
|
int totalBytes = 0;
|
||||||
|
for (int i = 0; i < dataBlocks.length; i++) {
|
||||||
|
totalBytes += dataBlocks[i].getNumDataCodewords();
|
||||||
|
}
|
||||||
|
byte[] resultBytes = new byte[totalBytes];
|
||||||
|
int resultOffset = 0;
|
||||||
|
|
||||||
|
// Error-correct and copy data blocks together into a stream of bytes
|
||||||
|
for (int j = 0; j < dataBlocks.length; j++) {
|
||||||
|
DataBlock dataBlock = dataBlocks[j];
|
||||||
|
byte[] codewordBytes = dataBlock.getCodewords();
|
||||||
|
int numDataCodewords = dataBlock.getNumDataCodewords();
|
||||||
|
correctErrors(codewordBytes, numDataCodewords);
|
||||||
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
|
resultBytes[resultOffset++] = codewordBytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the contents of that stream of bytes
|
||||||
|
return DecodedBitStreamParser.decode(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||||
|
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||||
|
*
|
||||||
|
* @param codewordBytes data and error correction codewords
|
||||||
|
* @param numDataCodewords number of codewords that are data bytes
|
||||||
|
* @throws ReaderException if error correction fails
|
||||||
|
*/
|
||||||
|
private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ReaderException {
|
||||||
|
int numCodewords = codewordBytes.length;
|
||||||
|
// First read into an array of ints
|
||||||
|
int[] codewordsInts = new int[numCodewords];
|
||||||
|
for (int i = 0; i < numCodewords; i++) {
|
||||||
|
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||||
|
}
|
||||||
|
int numECCodewords = codewordBytes.length - numDataCodewords;
|
||||||
|
try {
|
||||||
|
rsDecoder.decode(codewordsInts, numECCodewords);
|
||||||
|
} catch (ReedSolomonException rse) {
|
||||||
|
throw new ReaderException(rse.toString());
|
||||||
|
}
|
||||||
|
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||||
|
// We don't care about errors in the error-correction codewords
|
||||||
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
|
codewordBytes[i] = (byte) codewordsInts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
245
core/src/com/google/zxing/datamatrix/decoder/Version.java
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Version object encapsulates attributes about a particular
|
||||||
|
* size Data Matrix Code.
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class Version {
|
||||||
|
|
||||||
|
private static final Version[] VERSIONS = buildVersions();
|
||||||
|
|
||||||
|
private final int versionNumber;
|
||||||
|
private final int symbolSizeRows;
|
||||||
|
private final int symbolSizeColumns;
|
||||||
|
private final int dataRegionSizeRows;
|
||||||
|
private final int dataRegionSizeColumns;
|
||||||
|
private final ECBlocks ecBlocks;
|
||||||
|
private final int totalCodewords;
|
||||||
|
|
||||||
|
private Version(int versionNumber,
|
||||||
|
int symbolSizeRows,
|
||||||
|
int symbolSizeColumns,
|
||||||
|
int dataRegionSizeRows,
|
||||||
|
int dataRegionSizeColumns,
|
||||||
|
ECBlocks ecBlocks) {
|
||||||
|
this.versionNumber = versionNumber;
|
||||||
|
this.symbolSizeRows = symbolSizeRows;
|
||||||
|
this.symbolSizeColumns = symbolSizeColumns;
|
||||||
|
this.dataRegionSizeRows = dataRegionSizeRows;
|
||||||
|
this.dataRegionSizeColumns = dataRegionSizeColumns;
|
||||||
|
this.ecBlocks = ecBlocks;
|
||||||
|
|
||||||
|
// Calculate the total number of codewords
|
||||||
|
int total = 0;
|
||||||
|
int ecCodewords = ecBlocks.ecCodewords;
|
||||||
|
ECB[] ecbArray = ecBlocks.ecBlocks;
|
||||||
|
for (int i = 0; i < ecbArray.length; i++) {
|
||||||
|
ECB ecBlock = ecbArray[i];
|
||||||
|
total += ecBlock.count * (ecBlock.dataCodewords + ecCodewords);
|
||||||
|
}
|
||||||
|
this.totalCodewords = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVersionNumber() {
|
||||||
|
return versionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSymbolSizeRows() {
|
||||||
|
return symbolSizeRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSymbolSizeColumns() {
|
||||||
|
return symbolSizeColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataRegionSizeRows() {
|
||||||
|
return dataRegionSizeRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataRegionSizeColumns() {
|
||||||
|
return dataRegionSizeColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalCodewords() {
|
||||||
|
return totalCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECBlocks getECBlocks() {
|
||||||
|
return ecBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Deduces version information from Data Matrix dimensions.</p>
|
||||||
|
*
|
||||||
|
* @param numRows Number of rows in modules
|
||||||
|
* @param numRows Number of columns in modules
|
||||||
|
* @return {@link Version} for a Data Matrix Code of those dimensions
|
||||||
|
* @throws ReaderException if dimensions do correspond to a valid Data Matrix size
|
||||||
|
*/
|
||||||
|
public static Version getVersionForDimensions(int numRows, int numColumns) throws ReaderException {
|
||||||
|
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
|
||||||
|
throw new ReaderException("Dimension must be 0 mod 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bbrown): This is doing a linear search through the array of versions.
|
||||||
|
// If we interleave the rectangular versions with the square versions we could
|
||||||
|
// do a binary search.
|
||||||
|
int numVersions = VERSIONS.length;
|
||||||
|
for (int i = 0; i < numVersions; ++i){
|
||||||
|
Version version = VERSIONS[i];
|
||||||
|
if (version.symbolSizeRows == numRows) {
|
||||||
|
if (version.symbolSizeColumns == numColumns) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ReaderException("Dimensions do not correspond to a valid Data Matrix Version.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
|
||||||
|
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
|
||||||
|
* each set of blocks. It also holds the number of error-correction codewords per block since it
|
||||||
|
* will be the same across all blocks within one version.</p>
|
||||||
|
*/
|
||||||
|
static final class ECBlocks {
|
||||||
|
private int ecCodewords;
|
||||||
|
private ECB[] ecBlocks;
|
||||||
|
|
||||||
|
private ECBlocks(int ecCodewords, ECB ecBlocks) {
|
||||||
|
this.ecCodewords = ecCodewords;
|
||||||
|
this.ecBlocks = new ECB[] { ecBlocks };
|
||||||
|
}
|
||||||
|
|
||||||
|
private ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) {
|
||||||
|
this.ecCodewords = ecCodewords;
|
||||||
|
this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
int getECCodewords() {
|
||||||
|
return ecCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECB[] getECBlocks() {
|
||||||
|
return ecBlocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
|
||||||
|
* This includes the number of data codewords, and the number of times a block with these
|
||||||
|
* parameters is used consecutively in the Data Matrix code version's format.</p>
|
||||||
|
*/
|
||||||
|
static final class ECB {
|
||||||
|
private int count;
|
||||||
|
private int dataCodewords;
|
||||||
|
|
||||||
|
private ECB(int count, int dataCodewords) {
|
||||||
|
this.count = count;
|
||||||
|
this.dataCodewords = dataCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDataCodewords() {
|
||||||
|
return dataCodewords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(versionNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006 5.5.1 Table 7
|
||||||
|
*/
|
||||||
|
private static Version[] buildVersions() {
|
||||||
|
return new Version[]{
|
||||||
|
new Version(1, 10, 10, 8, 8,
|
||||||
|
new ECBlocks(5, new ECB(1, 3))),
|
||||||
|
new Version(2, 12, 12, 10, 10,
|
||||||
|
new ECBlocks(7, new ECB(1, 5))),
|
||||||
|
new Version(3, 14, 14, 12, 12,
|
||||||
|
new ECBlocks(10, new ECB(1, 8))),
|
||||||
|
new Version(4, 16, 16, 14, 14,
|
||||||
|
new ECBlocks(12, new ECB(1, 12))),
|
||||||
|
new Version(5, 18, 18, 16, 16,
|
||||||
|
new ECBlocks(14, new ECB(1, 18))),
|
||||||
|
new Version(6, 20, 20, 18, 18,
|
||||||
|
new ECBlocks(18, new ECB(1, 22))),
|
||||||
|
new Version(7, 22, 22, 20, 20,
|
||||||
|
new ECBlocks(20, new ECB(1, 30))),
|
||||||
|
new Version(8, 24, 24, 22, 22,
|
||||||
|
new ECBlocks(24, new ECB(1, 36))),
|
||||||
|
new Version(9, 26, 26, 24, 24,
|
||||||
|
new ECBlocks(28, new ECB(1, 44))),
|
||||||
|
new Version(10, 32, 32, 14, 14,
|
||||||
|
new ECBlocks(36, new ECB(1, 62))),
|
||||||
|
new Version(11, 36, 36, 16, 16,
|
||||||
|
new ECBlocks(42, new ECB(1, 86))),
|
||||||
|
new Version(12, 40, 40, 18, 18,
|
||||||
|
new ECBlocks(48, new ECB(1, 114))),
|
||||||
|
new Version(13, 44, 44, 20, 20,
|
||||||
|
new ECBlocks(56, new ECB(1, 144))),
|
||||||
|
new Version(14, 48, 48, 22, 22,
|
||||||
|
new ECBlocks(68, new ECB(1, 174))),
|
||||||
|
new Version(15, 52, 52, 24, 24,
|
||||||
|
new ECBlocks(42, new ECB(2, 102))),
|
||||||
|
new Version(16, 64, 64, 14, 14,
|
||||||
|
new ECBlocks(56, new ECB(2, 140))),
|
||||||
|
new Version(17, 72, 72, 16, 16,
|
||||||
|
new ECBlocks(36, new ECB(4, 92))),
|
||||||
|
new Version(18, 80, 80, 18, 18,
|
||||||
|
new ECBlocks(48, new ECB(4, 114))),
|
||||||
|
new Version(19, 88, 88, 20, 20,
|
||||||
|
new ECBlocks(56, new ECB(4, 144))),
|
||||||
|
new Version(20, 96, 96, 22, 22,
|
||||||
|
new ECBlocks(68, new ECB(4, 174))),
|
||||||
|
new Version(21, 104, 104, 24, 24,
|
||||||
|
new ECBlocks(56, new ECB(6, 136))),
|
||||||
|
new Version(22, 120, 120, 18, 18,
|
||||||
|
new ECBlocks(68, new ECB(6, 175))),
|
||||||
|
new Version(23, 132, 132, 20, 20,
|
||||||
|
new ECBlocks(62, new ECB(8, 163))),
|
||||||
|
new Version(24, 144, 144, 22, 22,
|
||||||
|
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),
|
||||||
|
new Version(25, 8, 18, 6, 16,
|
||||||
|
new ECBlocks(7, new ECB(1, 5))),
|
||||||
|
new Version(26, 8, 32, 6, 14,
|
||||||
|
new ECBlocks(11, new ECB(1, 10))),
|
||||||
|
new Version(27, 12, 26, 10, 24,
|
||||||
|
new ECBlocks(14, new ECB(1, 16))),
|
||||||
|
new Version(28, 12, 36, 10, 16,
|
||||||
|
new ECBlocks(18, new ECB(1, 22))),
|
||||||
|
new Version(29, 16, 36, 10, 16,
|
||||||
|
new ECBlocks(24, new ECB(1, 32))),
|
||||||
|
new Version(30, 16, 48, 14, 22,
|
||||||
|
new ECBlocks(28, new ECB(1, 49)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
core/src/com/google/zxing/datamatrix/detector/Detector.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.BlackPointEstimationMethod;
|
||||||
|
import com.google.zxing.MonochromeBitmapSource;
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.datamatrix.decoder.Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code
|
||||||
|
* is rotated or skewed, or partially obscured.</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class Detector {
|
||||||
|
|
||||||
|
private final MonochromeBitmapSource image;
|
||||||
|
|
||||||
|
public Detector(MonochromeBitmapSource image) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Detects a Data Matrix Code in an image, simply.</p>
|
||||||
|
*
|
||||||
|
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||||
|
* @throws ReaderException if no Data Matrix Code can be found
|
||||||
|
*/
|
||||||
|
public DetectorResult detect() throws ReaderException {
|
||||||
|
return new DetectorResult(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2007 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
|
||||||
|
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
|
||||||
|
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
|
||||||
|
*
|
||||||
|
* @author srowen@google.com (Sean Owen)
|
||||||
|
*/
|
||||||
|
public final class DetectorResult {
|
||||||
|
|
||||||
|
private final BitMatrix bits;
|
||||||
|
private final ResultPoint[] points;
|
||||||
|
|
||||||
|
DetectorResult(BitMatrix bits, ResultPoint[] points) {
|
||||||
|
this.bits = bits;
|
||||||
|
this.points = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix getBits() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultPoint[] getPoints() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.png
Normal file
After Width: | Height: | Size: 206 B |
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Hello World
|
After Width: | Height: | Size: 3 KiB |
|
@ -0,0 +1 @@
|
||||||
|
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd
|
BIN
core/test/data/blackbox/datamatrix-1/abcd-52x52.png.error
Normal file
After Width: | Height: | Size: 801 B |
1
core/test/data/blackbox/datamatrix-1/abcd-52x52.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd
|
BIN
core/test/data/blackbox/datamatrix-1/abcdefg-64x64.png.error
Normal file
After Width: | Height: | Size: 1.2 KiB |
1
core/test/data/blackbox/datamatrix-1/abcdefg-64x64.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\
|
BIN
core/test/data/blackbox/datamatrix-1/abcdefg.png
Normal file
After Width: | Height: | Size: 515 B |
1
core/test/data/blackbox/datamatrix-1/abcdefg.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\
|
BIN
core/test/data/blackbox/datamatrix-1/zxing_URL_L_Kayway.png
Normal file
After Width: | Height: | Size: 302 B |
|
@ -0,0 +1 @@
|
||||||
|
http://code.google.com/p/zxing/
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix;
|
||||||
|
|
||||||
|
//import com.google.zxing.MultiFormatReader;
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class DataMatrixBlackBox1TestCase extends AbstractBlackBoxTestCase {
|
||||||
|
|
||||||
|
public DataMatrixBlackBox1TestCase() {
|
||||||
|
super(new File("test/data/blackbox/datamatrix-1"), new DataMatrixReader(), 1.0, BarcodeFormat.DATAMATRIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008 Google Inc.
|
||||||
|
*
|
||||||
|
* 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.datamatrix.decoder;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import com.google.zxing.ReaderException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class DecodedBitStreamParserTestCase extends TestCase{
|
||||||
|
|
||||||
|
public void testAsciiStandardDecode() {
|
||||||
|
// ASCII characters 0-127 are encoded as the value + 1
|
||||||
|
byte[] bytes = new byte[]{(byte) ('a' + 1), (byte) ('b' + 1), (byte) ('c' + 1),
|
||||||
|
(byte) ('A' + 1), (byte) ('B' + 1), (byte) ('C' + 1)};
|
||||||
|
String decodedString = "";
|
||||||
|
try {
|
||||||
|
decodedString = DecodedBitStreamParser.decode(bytes);
|
||||||
|
} catch (ReaderException readerException) {
|
||||||
|
// TODO(bbrown): Throw this to junit?
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("abcABC", decodedString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAsciiDoubleDigitDecode() throws ReaderException{
|
||||||
|
// ASCII double digit (00 - 99) Numeric Value + 130
|
||||||
|
byte[] bytes = new byte[]{(byte) (00 + 130), (byte) (01 + 130),
|
||||||
|
(byte) (98 + 130), (byte) (99 + 130)};
|
||||||
|
String decodedString = "";
|
||||||
|
decodedString = DecodedBitStreamParser.decode(bytes);
|
||||||
|
|
||||||
|
assertEquals("00019899", decodedString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bbrown): Add test cases for each encoding type
|
||||||
|
// TODO(bbrown): Add test cases for switching encoding types
|
||||||
|
}
|