From 0ee5f7050379e4edac731b8bc15ff68b6e03949c Mon Sep 17 00:00:00 2001 From: briangbrown Date: Tue, 11 Mar 2008 03:48:12 +0000 Subject: [PATCH] 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: http://zxing.googlecode.com/svn/trunk@264 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../com/google/zxing/MultiFormatReader.java | 5 + .../zxing/datamatrix/DataMatrixReader.java | 124 +++++ .../datamatrix/decoder/BitMatrixParser.java | 446 +++++++++++++++++ .../zxing/datamatrix/decoder/DataBlock.java | 118 +++++ .../decoder/DecodedBitStreamParser.java | 462 ++++++++++++++++++ .../zxing/datamatrix/decoder/Decoder.java | 130 +++++ .../zxing/datamatrix/decoder/Version.java | 245 ++++++++++ .../zxing/datamatrix/detector/Detector.java | 51 ++ .../datamatrix/detector/DetectorResult.java | 47 ++ .../datamatrix-1/HelloWorld_Text_L_Kaywa.png | Bin 0 -> 206 bytes .../datamatrix-1/HelloWorld_Text_L_Kaywa.txt | 1 + .../HelloWorld_Text_L_Kaywa_1_error_byte.png | Bin 0 -> 3812 bytes .../HelloWorld_Text_L_Kaywa_1_error_byte.txt | 1 + .../HelloWorld_Text_L_Kaywa_2_error_byte.png | Bin 0 -> 3810 bytes .../HelloWorld_Text_L_Kaywa_2_error_byte.txt | 1 + .../HelloWorld_Text_L_Kaywa_3_error_byte.png | Bin 0 -> 3813 bytes .../HelloWorld_Text_L_Kaywa_3_error_byte.txt | 1 + .../HelloWorld_Text_L_Kaywa_4_error_byte.png | Bin 0 -> 3801 bytes .../HelloWorld_Text_L_Kaywa_4_error_byte.txt | 1 + ...oWorld_Text_L_Kaywa_6_error_byte.png.error | Bin 0 -> 3804 bytes .../HelloWorld_Text_L_Kaywa_6_error_byte.txt | 1 + .../abcd-52x52-IDAutomation.gif.error | Bin 0 -> 3070 bytes .../datamatrix-1/abcd-52x52-IDAutomation.txt | 1 + .../datamatrix-1/abcd-52x52.png.error | Bin 0 -> 801 bytes .../data/blackbox/datamatrix-1/abcd-52x52.txt | 1 + .../datamatrix-1/abcdefg-64x64.png.error | Bin 0 -> 1187 bytes .../blackbox/datamatrix-1/abcdefg-64x64.txt | 1 + .../data/blackbox/datamatrix-1/abcdefg.png | Bin 0 -> 515 bytes .../data/blackbox/datamatrix-1/abcdefg.txt | 1 + .../datamatrix-1/zxing_URL_L_Kayway.png | Bin 0 -> 302 bytes .../datamatrix-1/zxing_URL_L_Kayway.txt | 1 + .../DataMatrixBlackBox1TestCase.java | 34 ++ .../DecodedBitStreamParserTestCase.java | 54 ++ 33 files changed, 1727 insertions(+) create mode 100644 core/src/com/google/zxing/datamatrix/DataMatrixReader.java create mode 100644 core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java create mode 100644 core/src/com/google/zxing/datamatrix/decoder/DataBlock.java create mode 100644 core/src/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java create mode 100644 core/src/com/google/zxing/datamatrix/decoder/Decoder.java create mode 100644 core/src/com/google/zxing/datamatrix/decoder/Version.java create mode 100644 core/src/com/google/zxing/datamatrix/detector/Detector.java create mode 100644 core/src/com/google/zxing/datamatrix/detector/DetectorResult.java create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.png create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.txt create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_1_error_byte.png create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_1_error_byte.txt create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_2_error_byte.png create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_2_error_byte.txt create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.png create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.txt create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_4_error_byte.png create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_4_error_byte.txt create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_6_error_byte.png.error create mode 100644 core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_6_error_byte.txt create mode 100644 core/test/data/blackbox/datamatrix-1/abcd-52x52-IDAutomation.gif.error create mode 100644 core/test/data/blackbox/datamatrix-1/abcd-52x52-IDAutomation.txt create mode 100644 core/test/data/blackbox/datamatrix-1/abcd-52x52.png.error create mode 100644 core/test/data/blackbox/datamatrix-1/abcd-52x52.txt create mode 100644 core/test/data/blackbox/datamatrix-1/abcdefg-64x64.png.error create mode 100644 core/test/data/blackbox/datamatrix-1/abcdefg-64x64.txt create mode 100644 core/test/data/blackbox/datamatrix-1/abcdefg.png create mode 100644 core/test/data/blackbox/datamatrix-1/abcdefg.txt create mode 100644 core/test/data/blackbox/datamatrix-1/zxing_URL_L_Kayway.png create mode 100644 core/test/data/blackbox/datamatrix-1/zxing_URL_L_Kayway.txt create mode 100644 core/test/src/com/google/zxing/datamatrix/DataMatrixBlackBox1TestCase.java create mode 100644 core/test/src/com/google/zxing/datamatrix/decoder/DecodedBitStreamParserTestCase.java diff --git a/core/src/com/google/zxing/MultiFormatReader.java b/core/src/com/google/zxing/MultiFormatReader.java index 30324226..0409436a 100644 --- a/core/src/com/google/zxing/MultiFormatReader.java +++ b/core/src/com/google/zxing/MultiFormatReader.java @@ -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++) { diff --git a/core/src/com/google/zxing/datamatrix/DataMatrixReader.java b/core/src/com/google/zxing/datamatrix/DataMatrixReader.java new file mode 100644 index 00000000..5d7b4a30 --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/DataMatrixReader.java @@ -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; + } + +} \ No newline at end of file diff --git a/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java b/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java new file mode 100644 index 00000000..3bc61cd8 --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java @@ -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()); + } + + /** + *

Creates the version object based on the dimension of the original bit matrix from + * the datamatrix code.

+ * + *

See ISO 16022:2006 Table 7 - ECC 200 symbol attributes

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

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.

+ * + * @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; + } + + /** + *

Reads a bit of the mapping matrix accounting for boundry wrapping.

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

Reads the 8 bits of the standard utah shaped pattern.

+ * + *

See ISO 16022:2006, 5.8.1 Figure 6

+ * + * @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; + } + + /** + *

Reads the 8 bits of the special corner condition 1.

+ * + *

See ISO 16022:2006, Figure F.3

+ * + * @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; + } + + /** + *

Reads the 8 bits of the special corner condition 2.

+ * + *

See ISO 16022:2006, Figure F.4

+ * + * @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; + } + + /** + *

Reads the 8 bits of the special corner condition 3.

+ * + *

See ISO 16022:2006, Figure F.5

+ * + * @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; + } + + /** + *

Reads the 8 bits of the special corner condition 4.

+ * + *

See ISO 16022:2006, Figure F.6

+ * + * @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; + } + + /** + *

Extracts the data region from a {@link BitMatrix} that contains + * alignment patterns.

+ * + * @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; + } + +} \ No newline at end of file diff --git a/core/src/com/google/zxing/datamatrix/decoder/DataBlock.java b/core/src/com/google/zxing/datamatrix/decoder/DataBlock.java new file mode 100644 index 00000000..4bcbb3a7 --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/decoder/DataBlock.java @@ -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; + +/** + *

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.

+ * + * @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; + } + + /** + *

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.

+ * + * @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; + } + +} diff --git a/core/src/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java b/core/src/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java new file mode 100644 index 00000000..9d6f2079 --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java @@ -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; + +/** + *

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.

+ * + *

See ISO 16022:2006, 5.2.1 - 5.2.9.2

+ * + * @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 + result.append(""); + } 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); + } + } + +} diff --git a/core/src/com/google/zxing/datamatrix/decoder/Decoder.java b/core/src/com/google/zxing/datamatrix/decoder/Decoder.java new file mode 100644 index 00000000..3280eb62 --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/decoder/Decoder.java @@ -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; + +/** + *

The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting + * the Data Matrix Code from an image.

+ * + * @author bbrown@google.com (Brian Brown) + */ +public final class Decoder { + + private final ReedSolomonDecoder rsDecoder; + + public Decoder() { + rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD); + } + + /** + *

Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans. + * "true" is taken to mean a black module.

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

Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken + * to mean a black module.

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

Given data and error-correction codewords received, possibly corrupted by errors, attempts to + * correct the errors in-place using Reed-Solomon error correction.

+ * + * @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]; + } + } + +} diff --git a/core/src/com/google/zxing/datamatrix/decoder/Version.java b/core/src/com/google/zxing/datamatrix/decoder/Version.java new file mode 100644 index 00000000..dc6469b5 --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/decoder/Version.java @@ -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; + } + + /** + *

Deduces version information from Data Matrix dimensions.

+ * + * @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."); + } + + /** + *

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.

+ */ + 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; + } + } + + /** + *

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.

+ */ + 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))) + }; + } + +} diff --git a/core/src/com/google/zxing/datamatrix/detector/Detector.java b/core/src/com/google/zxing/datamatrix/detector/Detector.java new file mode 100644 index 00000000..e445976e --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/detector/Detector.java @@ -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; + +/** + *

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.

+ * + * @author bbrown@google.com (Brian Brown) + */ +public final class Detector { + + private final MonochromeBitmapSource image; + + public Detector(MonochromeBitmapSource image) { + this.image = image; + } + + /** + *

Detects a Data Matrix Code in an image, simply.

+ * + * @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); + } + + +} diff --git a/core/src/com/google/zxing/datamatrix/detector/DetectorResult.java b/core/src/com/google/zxing/datamatrix/detector/DetectorResult.java new file mode 100644 index 00000000..b755432d --- /dev/null +++ b/core/src/com/google/zxing/datamatrix/detector/DetectorResult.java @@ -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; + +/** + *

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.

+ * + * @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; + } + +} \ No newline at end of file diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.png b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.png new file mode 100644 index 0000000000000000000000000000000000000000..d5aeed121a1a058230d553f909f3eac5b14c646f GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^Js`};3?!%UU9JUEYymzYuK)l42QotsU9JOC&7Llf zAs(G)r(Wc0FyL@y{r|sTU&3;3_r2EJ?M=#eb3vvGmZm%mIbD@$W1n=!o2NL|0B-sO~D@42f6E2I}rRn++Qv+|_T1*xrf z*B$@zS9)#6Q>jphljR4wiow&>&t;ucLK6TK C0#w}q literal 0 HcmV?d00001 diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.txt b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.txt new file mode 100644 index 00000000..5e1c309d --- /dev/null +++ b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa.txt @@ -0,0 +1 @@ +Hello World \ No newline at end of file diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_1_error_byte.png b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_1_error_byte.png new file mode 100644 index 0000000000000000000000000000000000000000..df497444b5bdd35ee2ed771e254a33575c973ecb GIT binary patch literal 3812 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 Y08=+U2fq4F-v9sr07*qoM6N<$f*w5~TL1t6 literal 0 HcmV?d00001 diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_2_error_byte.txt b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_2_error_byte.txt new file mode 100644 index 00000000..5e1c309d --- /dev/null +++ b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_2_error_byte.txt @@ -0,0 +1 @@ +Hello World \ No newline at end of file diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.png b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.png new file mode 100644 index 0000000000000000000000000000000000000000..8fc17705807f057573254bf022708dc69a124c61 GIT binary patch literal 3813 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 byYm|WM?yOX`>bZE00000NkvXXu0mjf%i$$w literal 0 HcmV?d00001 diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.txt b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.txt new file mode 100644 index 00000000..5e1c309d --- /dev/null +++ b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_3_error_byte.txt @@ -0,0 +1 @@ +Hello World \ No newline at end of file diff --git a/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_4_error_byte.png b/core/test/data/blackbox/datamatrix-1/HelloWorld_Text_L_Kaywa_4_error_byte.png new file mode 100644 index 0000000000000000000000000000000000000000..7a2b152f12c3dc60e3ae8e99443b8ee767c90a41 GIT binary patch literal 3801 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 literal 0 HcmV?d00001 diff --git a/core/test/data/blackbox/datamatrix-1/abcd-52x52.txt b/core/test/data/blackbox/datamatrix-1/abcd-52x52.txt new file mode 100644 index 00000000..d01ae5a4 --- /dev/null +++ b/core/test/data/blackbox/datamatrix-1/abcd-52x52.txt @@ -0,0 +1 @@ +abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd \ No newline at end of file diff --git a/core/test/data/blackbox/datamatrix-1/abcdefg-64x64.png.error b/core/test/data/blackbox/datamatrix-1/abcdefg-64x64.png.error new file mode 100644 index 0000000000000000000000000000000000000000..de4f32d66e52dc14bc201afda7e1421c68404c62 GIT binary patch literal 1187 zcmV;U1YG-xP)Sb&FCB&BS}Tn2$@*Cn<5e0dPnbA0`? zEArjTh_#5D=W`pku6n}C=lxmB@ABu9o2#%Ejo62{^?@~PKQTc0-3^lhI|VgjC!!?z zJX59C3q=r5-vkc)mK(7YQM)PD$R{aRHhrt+&5?aZEJUdGRu_?IXe#V1469U|;)f9n z5%o1?IDRnx3zM2+)|*oU~S6(3m5O~@Zv$b)2~5&IAz;1eOF9t6)2JV~PN+D;>O zAvA7`FUJE>9YmzEIT^7NAz2FdS1nPx0R{SyW*H+kBFYoa8>jnEHx&idIAN+~#6HBE zD~;HR$ak+}#7+dW2ESnMTvSjE1uKe>M(R>VtV3X5JAyfd{p5(B%pqpdMMN2~4}qr= zjA}ujBalYci)n^8X~a&%l`^+6)n$G&4uA916}T~CBci&9TFLDYV)Lng~+2Gb8}SZnW(PN z5TkZv#74w@ccl?K5&7x|fjK$e_2oMw%%7ykj3MqE)88LX-qjKbyTfZlbCZuZHJN>u@8~xCRD52itH&g zNYsyUWW-KHsRhYyK68j1mdn9AijCNa&;WvNf~6oyAtb}}gp4v`Cj!bq7?~v?&tV!- zw_s)&u@He)QWmi+y5A&+7Otz^)SnT%5p)W|F=zn53>z9mh zp|*DPiAJnMJl$%^j)suZNpgMv5qyl;iD+G@PO=TPS$ua46$S&05gQS`8#m}GNx3ds z6LW|1!7U>;BJR5@jo69Ecdui_PQ+vMK=@!TK^j#lXWWboX%*;3Myx|<7D0lyQMyy1%QyfPx=TS_tG1`g-Tg-2>~;QP z8T{E+*l5OJ!#zf9L#VMlY7W{7aqucDnlAi1!W*#2Pa{?$YAE3r%_pov*J1ihvl3NC zY(#L680)$k$wI z?=P||bCb_U_0RRWeKyw4vq^R^kGE$Rk&L&(&pp|}enCi&a7K`2`wp@T3lYIptRU2} zBSLmznt7SQUHe!wvdPf=vY4y3TZ#(VWQH*jzZ#?p8bgF^G8FG~7gDw{TvboDG0kwo zMIvgQTiRcNY%#CU){Xa}T$XHOS}$dw&VpA1oUm z%6WA`v_`VQyse`V;%U@(l`~mqIIBEi_UeG=(S__}j=aPO!Ajo8A%<)+>*qRIF|G$t z^<+D9Y@q37dgh}DI@x5B`Iw#1S1zvfB->d_O>F}us~hnp8?54~d0sZjgv=BryRh7g z=w4vM zwsKY-p}#?GY--}DZy<@A7G)@o@)UklmDZm&8je$o%wA#CzMi{cq77)HFdph2m*=E3 zIx&3K6m{gNAdp}nZ