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
This commit is contained in:
briangbrown 2008-03-11 03:48:12 +00:00
parent c6b6bfb332
commit 916edd548e
33 changed files with 1727 additions and 0 deletions

View file

@ -18,6 +18,7 @@ package com.google.zxing;
import com.google.zxing.oned.MultiFormatOneDReader;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.datamatrix.DataMatrixReader;
import java.util.Hashtable;
import java.util.Vector;
@ -51,10 +52,14 @@ public final class MultiFormatReader implements Reader {
if (possibleFormats.contains(BarcodeFormat.QR_CODE)) {
readers.addElement(new QRCodeReader());
}
if (possibleFormats.contains(BarcodeFormat.DATAMATRIX)) {
readers.addElement(new DataMatrixReader());
}
}
if (readers.isEmpty()) {
readers.addElement(new MultiFormatOneDReader());
readers.addElement(new QRCodeReader());
readers.addElement(new DataMatrixReader());
}
for (int i = 0; i < readers.size(); i++) {

View 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;
}
}

View file

@ -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;
}
}

View 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;
}
}

View file

@ -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);
}
}
}

View 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];
}
}
}

View 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)))
};
}
}

View 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);
}
}

View file

@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

View file

@ -0,0 +1 @@
Hello World

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1 @@
Hello World

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1 @@
Hello World

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1 @@
Hello World

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1 @@
Hello World

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1 @@
Hello World

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1 @@
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

View file

@ -0,0 +1 @@
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

View file

@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./\

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

View file

@ -0,0 +1 @@
http://code.google.com/p/zxing/

View file

@ -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);
}
}

View file

@ -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
}