moved java files for pre-convert

This commit is contained in:
Henry Schimke
2022-08-20 12:00:19 -05:00
parent 4997291cb8
commit 1901c96559
2757 changed files with 57224 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.DetectorResult;
import com.google.zxing.datamatrix.decoder.Decoder;
import com.google.zxing.datamatrix.detector.Detector;
import java.util.List;
import java.util.Map;
/**
* 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 NotFoundException if a Data Matrix code cannot be found
* @throws FormatException if a Data Matrix code cannot be decoded
* @throws ChecksumException if error correction fails
*/
@Override
public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
return decode(image, null);
}
@Override
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
throws NotFoundException, ChecksumException, FormatException {
DecoderResult decoderResult;
ResultPoint[] points;
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
BitMatrix bits = extractPureBits(image.getBlackMatrix());
decoderResult = decoder.decode(bits);
points = NO_POINTS;
} else {
DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect();
decoderResult = decoder.decode(detectorResult.getBits());
points = detectorResult.getPoints();
}
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,
BarcodeFormat.DATA_MATRIX);
List<byte[]> byteSegments = decoderResult.getByteSegments();
if (byteSegments != null) {
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
}
String ecLevel = decoderResult.getECLevel();
if (ecLevel != null) {
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
}
result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]d" + decoderResult.getSymbologyModifier());
return result;
}
@Override
public void reset() {
// do nothing
}
/**
* This method detects a code in a "pure" image -- that is, pure monochrome image
* which contains only an unrotated, unskewed, image of a code, with some white border
* around it. This is a specialized method that works exceptionally fast in this special
* case.
*/
private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {
int[] leftTopBlack = image.getTopLeftOnBit();
int[] rightBottomBlack = image.getBottomRightOnBit();
if (leftTopBlack == null || rightBottomBlack == null) {
throw NotFoundException.getNotFoundInstance();
}
int moduleSize = moduleSize(leftTopBlack, image);
int top = leftTopBlack[1];
int bottom = rightBottomBlack[1];
int left = leftTopBlack[0];
int right = rightBottomBlack[0];
int matrixWidth = (right - left + 1) / moduleSize;
int matrixHeight = (bottom - top + 1) / moduleSize;
if (matrixWidth <= 0 || matrixHeight <= 0) {
throw NotFoundException.getNotFoundInstance();
}
// 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.
int nudge = moduleSize / 2;
top += nudge;
left += nudge;
// Now just read off the bits
BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);
for (int y = 0; y < matrixHeight; y++) {
int iOffset = top + y * moduleSize;
for (int x = 0; x < matrixWidth; x++) {
if (image.get(left + x * moduleSize, iOffset)) {
bits.set(x, y);
}
}
}
return bits;
}
private static int moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {
int width = image.getWidth();
int x = leftTopBlack[0];
int y = leftTopBlack[1];
while (x < width && image.get(x, y)) {
x++;
}
if (x == width) {
throw NotFoundException.getNotFoundInstance();
}
int moduleSize = x - leftTopBlack[0];
if (moduleSize == 0) {
throw NotFoundException.getNotFoundInstance();
}
return moduleSize;
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.datamatrix.encoder.DefaultPlacement;
import com.google.zxing.Dimension;
import com.google.zxing.datamatrix.encoder.ErrorCorrection;
import com.google.zxing.datamatrix.encoder.HighLevelEncoder;
import com.google.zxing.datamatrix.encoder.MinimalEncoder;
import com.google.zxing.datamatrix.encoder.SymbolInfo;
import com.google.zxing.datamatrix.encoder.SymbolShapeHint;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import java.util.Map;
import java.nio.charset.Charset;
/**
* This object renders a Data Matrix code as a BitMatrix 2D array of greyscale values.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Guillaume Le Biller Added to zxing lib.
*/
public final class DataMatrixWriter implements Writer {
@Override
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {
return encode(contents, format, width, height, null);
}
@Override
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {
if (contents.isEmpty()) {
throw new IllegalArgumentException("Found empty contents");
}
if (format != BarcodeFormat.DATA_MATRIX) {
throw new IllegalArgumentException("Can only encode DATA_MATRIX, but got " + format);
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Requested dimensions can't be negative: " + width + 'x' + height);
}
// Try to get force shape & min / max size
SymbolShapeHint shape = SymbolShapeHint.FORCE_NONE;
Dimension minSize = null;
Dimension maxSize = null;
if (hints != null) {
SymbolShapeHint requestedShape = (SymbolShapeHint) hints.get(EncodeHintType.DATA_MATRIX_SHAPE);
if (requestedShape != null) {
shape = requestedShape;
}
@SuppressWarnings("deprecation")
Dimension requestedMinSize = (Dimension) hints.get(EncodeHintType.MIN_SIZE);
if (requestedMinSize != null) {
minSize = requestedMinSize;
}
@SuppressWarnings("deprecation")
Dimension requestedMaxSize = (Dimension) hints.get(EncodeHintType.MAX_SIZE);
if (requestedMaxSize != null) {
maxSize = requestedMaxSize;
}
}
//1. step: Data encodation
String encoded;
boolean hasCompactionHint = hints != null && hints.containsKey(EncodeHintType.DATA_MATRIX_COMPACT) &&
Boolean.parseBoolean(hints.get(EncodeHintType.DATA_MATRIX_COMPACT).toString());
if (hasCompactionHint) {
boolean hasGS1FormatHint = hints.containsKey(EncodeHintType.GS1_FORMAT) &&
Boolean.parseBoolean(hints.get(EncodeHintType.GS1_FORMAT).toString());
Charset charset = null;
boolean hasEncodingHint = hints.containsKey(EncodeHintType.CHARACTER_SET);
if (hasEncodingHint) {
charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());
}
encoded = MinimalEncoder.encodeHighLevel(contents, charset, hasGS1FormatHint ? 0x1D : -1, shape);
} else {
boolean hasForceC40Hint = hints != null && hints.containsKey(EncodeHintType.FORCE_C40) &&
Boolean.parseBoolean(hints.get(EncodeHintType.FORCE_C40).toString());
encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize, hasForceC40Hint);
}
SymbolInfo symbolInfo = SymbolInfo.lookup(encoded.length(), shape, minSize, maxSize, true);
//2. step: ECC generation
String codewords = ErrorCorrection.encodeECC200(encoded, symbolInfo);
//3. step: Module placement in Matrix
DefaultPlacement placement =
new DefaultPlacement(codewords, symbolInfo.getSymbolDataWidth(), symbolInfo.getSymbolDataHeight());
placement.place();
//4. step: low-level encoding
return encodeLowLevel(placement, symbolInfo, width, height);
}
/**
* Encode the given symbol info to a bit matrix.
*
* @param placement The DataMatrix placement.
* @param symbolInfo The symbol info to encode.
* @return The bit matrix generated.
*/
private static BitMatrix encodeLowLevel(DefaultPlacement placement, SymbolInfo symbolInfo, int width, int height) {
int symbolWidth = symbolInfo.getSymbolDataWidth();
int symbolHeight = symbolInfo.getSymbolDataHeight();
ByteMatrix matrix = new ByteMatrix(symbolInfo.getSymbolWidth(), symbolInfo.getSymbolHeight());
int matrixY = 0;
for (int y = 0; y < symbolHeight; y++) {
// Fill the top edge with alternate 0 / 1
int matrixX;
if ((y % symbolInfo.matrixHeight) == 0) {
matrixX = 0;
for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
matrix.set(matrixX, matrixY, (x % 2) == 0);
matrixX++;
}
matrixY++;
}
matrixX = 0;
for (int x = 0; x < symbolWidth; x++) {
// Fill the right edge with full 1
if ((x % symbolInfo.matrixWidth) == 0) {
matrix.set(matrixX, matrixY, true);
matrixX++;
}
matrix.set(matrixX, matrixY, placement.getBit(x, y));
matrixX++;
// Fill the right edge with alternate 0 / 1
if ((x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1) {
matrix.set(matrixX, matrixY, (y % 2) == 0);
matrixX++;
}
}
matrixY++;
// Fill the bottom edge with full 1
if ((y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1) {
matrixX = 0;
for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
matrix.set(matrixX, matrixY, true);
matrixX++;
}
matrixY++;
}
}
return convertByteMatrixToBitMatrix(matrix, width, height);
}
/**
* Convert the ByteMatrix to BitMatrix.
*
* @param reqHeight The requested height of the image (in pixels) with the Datamatrix code
* @param reqWidth The requested width of the image (in pixels) with the Datamatrix code
* @param matrix The input matrix.
* @return The output matrix.
*/
private static BitMatrix convertByteMatrixToBitMatrix(ByteMatrix matrix, int reqWidth, int reqHeight) {
int matrixWidth = matrix.getWidth();
int matrixHeight = matrix.getHeight();
int outputWidth = Math.max(reqWidth, matrixWidth);
int outputHeight = Math.max(reqHeight, matrixHeight);
int multiple = Math.min(outputWidth / matrixWidth, outputHeight / matrixHeight);
int leftPadding = (outputWidth - (matrixWidth * multiple)) / 2 ;
int topPadding = (outputHeight - (matrixHeight * multiple)) / 2 ;
BitMatrix output;
// remove padding if requested width and height are too small
if (reqHeight < matrixHeight || reqWidth < matrixWidth) {
leftPadding = 0;
topPadding = 0;
output = new BitMatrix(matrixWidth, matrixHeight);
} else {
output = new BitMatrix(reqWidth, reqHeight);
}
output.clear();
for (int inputY = 0, outputY = topPadding; inputY < matrixHeight; inputY++, outputY += multiple) {
// Write the contents of this row of the bytematrix
for (int inputX = 0, outputX = leftPadding; inputX < matrixWidth; inputX++, outputX += multiple) {
if (matrix.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
}

View File

@@ -0,0 +1,443 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
/**
* @author bbrown@google.com (Brian Brown)
*/
final class BitMatrixParser {
private final BitMatrix mappingBitMatrix;
private final BitMatrix readMappingMatrix;
private final Version version;
/**
* @param bitMatrix {@link BitMatrix} to parse
* @throws FormatException if dimension is < 8 or > 144 or not 0 mod 2
*/
BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
int dimension = bitMatrix.getHeight();
if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) {
throw FormatException.getFormatInstance();
}
version = readVersion(bitMatrix);
this.mappingBitMatrix = extractDataRegion(bitMatrix);
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getWidth(), this.mappingBitMatrix.getHeight());
}
Version getVersion() {
return version;
}
/**
* <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 FormatException if the dimensions of the mapping matrix are not valid
* Data Matrix dimensions.
*/
private static Version readVersion(BitMatrix bitMatrix) throws FormatException {
int numRows = bitMatrix.getHeight();
int numColumns = bitMatrix.getWidth();
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 FormatException if the exact number of bytes expected is not read
*/
byte[] readCodewords() throws FormatException {
byte[] result = new byte[version.getTotalCodewords()];
int resultOffset = 0;
int row = 4;
int column = 0;
int numRows = mappingBitMatrix.getHeight();
int numColumns = mappingBitMatrix.getWidth();
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(column, row)) {
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
}
row -= 2;
column += 2;
} while ((row >= 0) && (column < numColumns));
row += 1;
column += 3;
// Sweep downward diagonally to the left
do {
if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(column, row)) {
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 FormatException.getFormatInstance();
}
return result;
}
/**
* <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
*
* @param row Row to read in the mapping matrix
* @param column Column to read in the mapping matrix
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return value of the given bit in the mapping matrix
*/
private boolean readModule(int row, int column, int numRows, int numColumns) {
// Adjust the row and column indices based on boundary wrapping
if (row < 0) {
row += numRows;
column += 4 - ((numRows + 4) & 0x07);
}
if (column < 0) {
column += numColumns;
row += 4 - ((numColumns + 4) & 0x07);
}
if (row >= numRows) {
row -= numRows;
}
readMappingMatrix.set(column, row);
return mappingBitMatrix.get(column, row);
}
/**
* <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>
*
* <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
*
* @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
* @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the utah shape
*/
private 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 numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 1
*/
private 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 numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 2
*/
private 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 numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 3
*/
private 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 numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 4
*/
private 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 bitMatrix Original {@link BitMatrix} with alignment patterns
* @return BitMatrix that has the alignment patterns removed
*/
private BitMatrix extractDataRegion(BitMatrix bitMatrix) {
int symbolSizeRows = version.getSymbolSizeRows();
int symbolSizeColumns = version.getSymbolSizeColumns();
if (bitMatrix.getHeight() != symbolSizeRows) {
throw new IllegalArgumentException("Dimension of bitMatrix 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;
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow);
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
for (int i = 0; i < dataRegionSizeRows; ++i) {
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
int writeRowOffset = dataRegionRowOffset + i;
for (int j = 0; j < dataRegionSizeColumns; ++j) {
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
if (bitMatrix.get(readColumnOffset, readRowOffset)) {
int writeColumnOffset = dataRegionColumnOffset + j;
bitMatrixWithoutAlignment.set(writeColumnOffset, writeRowOffset);
}
}
}
}
}
return bitMatrixWithoutAlignment;
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.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 DataBlocks 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 (Version.ECB ecBlock : ecBlockArray) {
totalBlocks += ecBlock.getCount();
}
// Now establish DataBlocks of the appropriate size and number of data codewords
DataBlock[] result = new DataBlock[totalBlocks];
int numResultBlocks = 0;
for (Version.ECB ecBlock : ecBlockArray) {
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 jOffset = specialVersion ? (j + 8) % numResultBlocks : j;
int iOffset = specialVersion && jOffset > 7 ? i - 1 : i;
result[jOffset].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
}
}
if (rawCodewordsOffset != rawCodewords.length) {
throw new IllegalArgumentException();
}
return result;
}
int getNumDataCodewords() {
return numDataCodewords;
}
byte[] getCodewords() {
return codewords;
}
}

View File

@@ -0,0 +1,590 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitSource;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.ECIStringBuilder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* <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)
* @author Sean Owen
*/
final class DecodedBitStreamParser {
private enum Mode {
PAD_ENCODE, // Not really a mode
ASCII_ENCODE,
C40_ENCODE,
TEXT_ENCODE,
ANSIX12_ENCODE,
EDIFACT_ENCODE,
BASE256_ENCODE,
ECI_ENCODE
}
/**
* 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 = {
'*', '*', '*', ' ', '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 = {
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
};
/**
* 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 = {
'*', '*', '*', ' ', '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'
};
// Shift 2 for Text is the same encoding as C40
private static final char[] TEXT_SHIFT2_SET_CHARS = C40_SHIFT2_SET_CHARS;
private static final char[] TEXT_SHIFT3_SET_CHARS = {
'`', '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', '{', '|', '}', '~', (char) 127
};
private DecodedBitStreamParser() {
}
static DecoderResult decode(byte[] bytes) throws FormatException {
BitSource bits = new BitSource(bytes);
ECIStringBuilder result = new ECIStringBuilder(100);
StringBuilder resultTrailer = new StringBuilder(0);
List<byte[]> byteSegments = new ArrayList<>(1);
Mode mode = Mode.ASCII_ENCODE;
// Could look directly at 'bytes', if we're sure of not having to account for multi byte values
Set<Integer> fnc1Positions = new HashSet<>();
int symbologyModifier;
boolean isECIencoded = false;
do {
if (mode == Mode.ASCII_ENCODE) {
mode = decodeAsciiSegment(bits, result, resultTrailer, fnc1Positions);
} else {
switch (mode) {
case C40_ENCODE:
decodeC40Segment(bits, result, fnc1Positions);
break;
case TEXT_ENCODE:
decodeTextSegment(bits, result, fnc1Positions);
break;
case ANSIX12_ENCODE:
decodeAnsiX12Segment(bits, result);
break;
case EDIFACT_ENCODE:
decodeEdifactSegment(bits, result);
break;
case BASE256_ENCODE:
decodeBase256Segment(bits, result, byteSegments);
break;
case ECI_ENCODE:
decodeECISegment(bits, result);
isECIencoded = true; // ECI detection only, atm continue decoding as ASCII
break;
default:
throw FormatException.getFormatInstance();
}
mode = Mode.ASCII_ENCODE;
}
} while (mode != Mode.PAD_ENCODE && bits.available() > 0);
if (resultTrailer.length() > 0) {
result.appendCharacters(resultTrailer);
}
if (isECIencoded) {
// Examples for this numbers can be found in this documentation of a hardware barcode scanner:
// https://honeywellaidc.force.com/supportppr/s/article/List-of-barcode-symbology-AIM-Identifiers
if (fnc1Positions.contains(0) || fnc1Positions.contains(4)) {
symbologyModifier = 5;
} else if (fnc1Positions.contains(1) || fnc1Positions.contains(5)) {
symbologyModifier = 6;
} else {
symbologyModifier = 4;
}
} else {
if (fnc1Positions.contains(0) || fnc1Positions.contains(4)) {
symbologyModifier = 2;
} else if (fnc1Positions.contains(1) || fnc1Positions.contains(5)) {
symbologyModifier = 3;
} else {
symbologyModifier = 1;
}
}
return new DecoderResult(bytes,
result.toString(),
byteSegments.isEmpty() ? null : byteSegments,
null,
symbologyModifier);
}
/**
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
*/
private static Mode decodeAsciiSegment(BitSource bits,
ECIStringBuilder result,
StringBuilder resultTrailer,
Set<Integer> fnc1positions) throws FormatException {
boolean upperShift = false;
do {
int oneByte = bits.readBits(8);
if (oneByte == 0) {
throw FormatException.getFormatInstance();
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
if (upperShift) {
oneByte += 128;
//upperShift = false;
}
result.append((char) (oneByte - 1));
return Mode.ASCII_ENCODE;
} else if (oneByte == 129) { // Pad
return Mode.PAD_ENCODE;
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
int value = oneByte - 130;
if (value < 10) { // pad with '0' for single digit values
result.append('0');
}
result.append(value);
} else {
switch (oneByte) {
case 230: // Latch to C40 encodation
return Mode.C40_ENCODE;
case 231: // Latch to Base 256 encodation
return Mode.BASE256_ENCODE;
case 232: // FNC1
fnc1positions.add(result.length());
result.append((char) 29); // translate as ASCII 29
break;
case 233: // Structured Append
case 234: // Reader Programming
// Ignore these symbols for now
//throw ReaderException.getInstance();
break;
case 235: // Upper Shift (shift to Extended ASCII)
upperShift = true;
break;
case 236: // 05 Macro
result.append("[)>\u001E05\u001D");
resultTrailer.insert(0, "\u001E\u0004");
break;
case 237: // 06 Macro
result.append("[)>\u001E06\u001D");
resultTrailer.insert(0, "\u001E\u0004");
break;
case 238: // Latch to ANSI X12 encodation
return Mode.ANSIX12_ENCODE;
case 239: // Latch to Text encodation
return Mode.TEXT_ENCODE;
case 240: // Latch to EDIFACT encodation
return Mode.EDIFACT_ENCODE;
case 241: // ECI Character
return Mode.ECI_ENCODE;
default:
// Not to be used in ASCII encodation
// but work around encoders that end with 254, latch back to ASCII
if (oneByte != 254 || bits.available() != 0) {
throw FormatException.getFormatInstance();
}
break;
}
}
} while (bits.available() > 0);
return Mode.ASCII_ENCODE;
}
/**
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
*/
private static void decodeC40Segment(BitSource bits, ECIStringBuilder result, Set<Integer> fnc1positions)
throws FormatException {
// Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
boolean upperShift = false;
int[] cValues = new int[3];
int shift = 0;
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8) {
return;
}
int firstByte = bits.readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (int i = 0; i < 3; i++) {
int cValue = cValues[i];
switch (shift) {
case 0:
if (cValue < 3) {
shift = cValue + 1;
} else if (cValue < C40_BASIC_SET_CHARS.length) {
char c40char = C40_BASIC_SET_CHARS[cValue];
if (upperShift) {
result.append((char) (c40char + 128));
upperShift = false;
} else {
result.append(c40char);
}
} else {
throw FormatException.getFormatInstance();
}
break;
case 1:
if (upperShift) {
result.append((char) (cValue + 128));
upperShift = false;
} else {
result.append((char) cValue);
}
shift = 0;
break;
case 2:
if (cValue < C40_SHIFT2_SET_CHARS.length) {
char c40char = C40_SHIFT2_SET_CHARS[cValue];
if (upperShift) {
result.append((char) (c40char + 128));
upperShift = false;
} else {
result.append(c40char);
}
} else {
switch (cValue) {
case 27: // FNC1
fnc1positions.add(result.length());
result.append((char) 29); // translate as ASCII 29
break;
case 30: // Upper Shift
upperShift = true;
break;
default:
throw FormatException.getFormatInstance();
}
}
shift = 0;
break;
case 3:
if (upperShift) {
result.append((char) (cValue + 224));
upperShift = false;
} else {
result.append((char) (cValue + 96));
}
shift = 0;
break;
default:
throw FormatException.getFormatInstance();
}
}
} while (bits.available() > 0);
}
/**
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
*/
private static void decodeTextSegment(BitSource bits, ECIStringBuilder result, Set<Integer> fnc1positions)
throws FormatException {
// Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
boolean upperShift = false;
int[] cValues = new int[3];
int shift = 0;
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8) {
return;
}
int firstByte = bits.readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (int i = 0; i < 3; i++) {
int cValue = cValues[i];
switch (shift) {
case 0:
if (cValue < 3) {
shift = cValue + 1;
} else if (cValue < TEXT_BASIC_SET_CHARS.length) {
char textChar = TEXT_BASIC_SET_CHARS[cValue];
if (upperShift) {
result.append((char) (textChar + 128));
upperShift = false;
} else {
result.append(textChar);
}
} else {
throw FormatException.getFormatInstance();
}
break;
case 1:
if (upperShift) {
result.append((char) (cValue + 128));
upperShift = false;
} else {
result.append((char) cValue);
}
shift = 0;
break;
case 2:
// Shift 2 for Text is the same encoding as C40
if (cValue < TEXT_SHIFT2_SET_CHARS.length) {
char textChar = TEXT_SHIFT2_SET_CHARS[cValue];
if (upperShift) {
result.append((char) (textChar + 128));
upperShift = false;
} else {
result.append(textChar);
}
} else {
switch (cValue) {
case 27: // FNC1
fnc1positions.add(result.length());
result.append((char) 29); // translate as ASCII 29
break;
case 30: // Upper Shift
upperShift = true;
break;
default:
throw FormatException.getFormatInstance();
}
}
shift = 0;
break;
case 3:
if (cValue < TEXT_SHIFT3_SET_CHARS.length) {
char textChar = TEXT_SHIFT3_SET_CHARS[cValue];
if (upperShift) {
result.append((char) (textChar + 128));
upperShift = false;
} else {
result.append(textChar);
}
shift = 0;
} else {
throw FormatException.getFormatInstance();
}
break;
default:
throw FormatException.getFormatInstance();
}
}
} while (bits.available() > 0);
}
/**
* See ISO 16022:2006, 5.2.7
*/
private static void decodeAnsiX12Segment(BitSource bits,
ECIStringBuilder result) throws FormatException {
// Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
int[] cValues = new int[3];
do {
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8) {
return;
}
int firstByte = bits.readBits(8);
if (firstByte == 254) { // Unlatch codeword
return;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (int i = 0; i < 3; i++) {
int cValue = cValues[i];
switch (cValue) {
case 0: // X12 segment terminator <CR>
result.append('\r');
break;
case 1: // X12 segment separator *
result.append('*');
break;
case 2: // X12 sub-element separator >
result.append('>');
break;
case 3: // space
result.append(' ');
break;
default:
if (cValue < 14) { // 0 - 9
result.append((char) (cValue + 44));
} else if (cValue < 40) { // A - Z
result.append((char) (cValue + 51));
} else {
throw FormatException.getFormatInstance();
}
break;
}
}
} while (bits.available() > 0);
}
private static void parseTwoBytes(int firstByte, int secondByte, int[] result) {
int fullBitValue = (firstByte << 8) + secondByte - 1;
int temp = fullBitValue / 1600;
result[0] = temp;
fullBitValue -= temp * 1600;
temp = fullBitValue / 40;
result[1] = temp;
result[2] = fullBitValue - temp * 40;
}
/**
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3
*/
private static void decodeEdifactSegment(BitSource bits, ECIStringBuilder result) {
do {
// If there is only two or less bytes left then it will be encoded as ASCII
if (bits.available() <= 16) {
return;
}
for (int i = 0; i < 4; i++) {
int edifactValue = bits.readBits(6);
// Check for the unlatch character
if (edifactValue == 0x1F) { // 011111
// Read rest of byte, which should be 0, and stop
int bitsLeft = 8 - bits.getBitOffset();
if (bitsLeft != 8) {
bits.readBits(bitsLeft);
}
return;
}
if ((edifactValue & 0x20) == 0) { // no 1 in the leading (6th) bit
edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value
}
result.append((char) edifactValue);
}
} while (bits.available() > 0);
}
/**
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
*/
private static void decodeBase256Segment(BitSource bits,
ECIStringBuilder result,
Collection<byte[]> byteSegments)
throws FormatException {
// Figure out how long the Base 256 Segment is.
int codewordPosition = 1 + bits.getByteOffset(); // position is 1-indexed
int d1 = unrandomize255State(bits.readBits(8), codewordPosition++);
int count;
if (d1 == 0) { // Read the remainder of the symbol
count = bits.available() / 8;
} else if (d1 < 250) {
count = d1;
} else {
count = 250 * (d1 - 249) + unrandomize255State(bits.readBits(8), codewordPosition++);
}
// We're seeing NegativeArraySizeException errors from users.
if (count < 0) {
throw FormatException.getFormatInstance();
}
byte[] bytes = new byte[count];
for (int i = 0; i < count; i++) {
// Have seen this particular error in the wild, such as at
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
bytes[i] = (byte) unrandomize255State(bits.readBits(8), codewordPosition++);
}
byteSegments.add(bytes);
result.append(new String(bytes, StandardCharsets.ISO_8859_1));
}
/**
* See ISO 16022:2007, 5.4.1
*/
private static void decodeECISegment(BitSource bits,
ECIStringBuilder result)
throws FormatException {
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
int c1 = bits.readBits(8);
if (c1 <= 127) {
result.appendECI(c1 - 1);
}
//currently we only support character set ECIs
/*} else {
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
int c2 = bits.readBits(8);
if (c1 >= 128 && c1 <= 191) {
} else {
if (bits.available() < 8) {
throw FormatException.getFormatInstance();
}
int c3 = bits.readBits(8);
}
}*/
}
/**
* See ISO 16022:2006, Annex B, B.2
*/
private static int unrandomize255State(int randomizedBase256Codeword,
int base256CodewordPosition) {
int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
return tempVariable >= 0 ? tempVariable : tempVariable + 256;
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DecoderResult;
import com.google.zxing.common.reedsolomon.GenericGF;
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(GenericGF.DATA_MATRIX_FIELD_256);
}
/**
* <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 and bytes encoded within the Data Matrix Code
* @throws FormatException if the Data Matrix Code cannot be decoded
* @throws ChecksumException if error correction fails
*/
public DecoderResult decode(boolean[][] image) throws FormatException, ChecksumException {
return decode(BitMatrix.parse(image));
}
/**
* <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 and bytes encoded within the Data Matrix Code
* @throws FormatException if the Data Matrix Code cannot be decoded
* @throws ChecksumException if error correction fails
*/
public DecoderResult decode(BitMatrix bits) throws FormatException, ChecksumException {
// Construct a parser and read version, error-correction level
BitMatrixParser parser = new BitMatrixParser(bits);
Version version = parser.getVersion();
// 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 (DataBlock db : dataBlocks) {
totalBytes += db.getNumDataCodewords();
}
byte[] resultBytes = new byte[totalBytes];
int dataBlocksCount = dataBlocks.length;
// Error-correct and copy data blocks together into a stream of bytes
for (int j = 0; j < dataBlocksCount; j++) {
DataBlock dataBlock = dataBlocks[j];
byte[] codewordBytes = dataBlock.getCodewords();
int numDataCodewords = dataBlock.getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords);
for (int i = 0; i < numDataCodewords; i++) {
// De-interlace data blocks.
resultBytes[i * dataBlocksCount + j] = 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 ChecksumException if error correction fails
*/
private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {
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;
}
try {
rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords);
} catch (ReedSolomonException ignored) {
throw ChecksumException.getChecksumInstance();
}
// 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,276 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.decoder;
import com.google.zxing.FormatException;
/**
* 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.getECCodewords();
ECB[] ecbArray = ecBlocks.getECBlocks();
for (ECB ecBlock : ecbArray) {
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + 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 numColumns Number of columns in modules
* @return Version for a Data Matrix Code of those dimensions
* @throws FormatException if dimensions do correspond to a valid Data Matrix size
*/
public static Version getVersionForDimensions(int numRows, int numColumns) throws FormatException {
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
throw FormatException.getFormatInstance();
}
for (Version version : VERSIONS) {
if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) {
return version;
}
}
throw FormatException.getFormatInstance();
}
/**
* <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 final int ecCodewords;
private final 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>Encapsulates 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 final int count;
private final int dataCodewords;
private ECB(int count, int dataCodewords) {
this.count = count;
this.dataCodewords = dataCodewords;
}
int getCount() {
return count;
}
int getDataCodewords() {
return dataCodewords;
}
}
@Override
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, 14, 16,
new ECBlocks(24, new ECB(1, 32))),
new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49))),
// extended forms as specified in
// ISO 21471:2020 (DMRE) 5.5.1 Table 7
new Version(31, 8, 48, 6, 22,
new ECBlocks(15, new ECB(1, 18))),
new Version(32, 8, 64, 6, 14,
new ECBlocks(18, new ECB(1, 24))),
new Version(33, 8, 80, 6, 18,
new ECBlocks(22, new ECB(1, 32))),
new Version(34, 8, 96, 6, 22,
new ECBlocks(28, new ECB(1, 38))),
new Version(35, 8, 120, 6, 18,
new ECBlocks(32, new ECB(1, 49))),
new Version(36, 8, 144, 6, 22,
new ECBlocks(36, new ECB(1, 63))),
new Version(37, 12, 64, 10, 14,
new ECBlocks(27, new ECB(1, 43))),
new Version(38, 12, 88, 10, 20,
new ECBlocks(36, new ECB(1, 64))),
new Version(39, 16, 64, 14, 14,
new ECBlocks(36, new ECB(1, 62))),
new Version(40, 20, 36, 18, 16,
new ECBlocks(28, new ECB(1, 44))),
new Version(41, 20, 44, 18, 20,
new ECBlocks(34, new ECB(1, 56))),
new Version(42, 20, 64, 18, 14,
new ECBlocks(42, new ECB(1, 84))),
new Version(43, 22, 48, 20, 22,
new ECBlocks(38, new ECB(1, 72))),
new Version(44, 24, 48, 22, 22,
new ECBlocks(41, new ECB(1, 80))),
new Version(45, 24, 64, 22, 14,
new ECBlocks(46, new ECB(1, 108))),
new Version(46, 26, 40, 24, 18,
new ECBlocks(38, new ECB(1, 70))),
new Version(47, 26, 48, 24, 22,
new ECBlocks(42, new ECB(1, 90))),
new Version(48, 26, 64, 24, 14,
new ECBlocks(50, new ECB(1, 118)))
};
}
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.detector;
import com.google.zxing.NotFoundException;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DetectorResult;
import com.google.zxing.common.GridSampler;
import com.google.zxing.common.detector.WhiteRectangleDetector;
/**
* <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 Sean Owen
*/
public final class Detector {
private final BitMatrix image;
private final WhiteRectangleDetector rectangleDetector;
public Detector(BitMatrix image) throws NotFoundException {
this.image = image;
rectangleDetector = new WhiteRectangleDetector(image);
}
/**
* <p>Detects a Data Matrix Code in an image.</p>
*
* @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code
* @throws NotFoundException if no Data Matrix Code can be found
*/
public DetectorResult detect() throws NotFoundException {
ResultPoint[] cornerPoints = rectangleDetector.detect();
ResultPoint[] points = detectSolid1(cornerPoints);
points = detectSolid2(points);
points[3] = correctTopRight(points);
if (points[3] == null) {
throw NotFoundException.getNotFoundInstance();
}
points = shiftToModuleCenter(points);
ResultPoint topLeft = points[0];
ResultPoint bottomLeft = points[1];
ResultPoint bottomRight = points[2];
ResultPoint topRight = points[3];
int dimensionTop = transitionsBetween(topLeft, topRight) + 1;
int dimensionRight = transitionsBetween(bottomRight, topRight) + 1;
if ((dimensionTop & 0x01) == 1) {
dimensionTop += 1;
}
if ((dimensionRight & 0x01) == 1) {
dimensionRight += 1;
}
if (4 * dimensionTop < 6 * dimensionRight && 4 * dimensionRight < 6 * dimensionTop) {
// The matrix is square
dimensionTop = dimensionRight = Math.max(dimensionTop, dimensionRight);
}
BitMatrix bits = sampleGrid(image,
topLeft,
bottomLeft,
bottomRight,
topRight,
dimensionTop,
dimensionRight);
return new DetectorResult(bits, new ResultPoint[]{topLeft, bottomLeft, bottomRight, topRight});
}
private static ResultPoint shiftPoint(ResultPoint point, ResultPoint to, int div) {
float x = (to.getX() - point.getX()) / (div + 1);
float y = (to.getY() - point.getY()) / (div + 1);
return new ResultPoint(point.getX() + x, point.getY() + y);
}
private static ResultPoint moveAway(ResultPoint point, float fromX, float fromY) {
float x = point.getX();
float y = point.getY();
if (x < fromX) {
x -= 1;
} else {
x += 1;
}
if (y < fromY) {
y -= 1;
} else {
y += 1;
}
return new ResultPoint(x, y);
}
/**
* Detect a solid side which has minimum transition.
*/
private ResultPoint[] detectSolid1(ResultPoint[] cornerPoints) {
// 0 2
// 1 3
ResultPoint pointA = cornerPoints[0];
ResultPoint pointB = cornerPoints[1];
ResultPoint pointC = cornerPoints[3];
ResultPoint pointD = cornerPoints[2];
int trAB = transitionsBetween(pointA, pointB);
int trBC = transitionsBetween(pointB, pointC);
int trCD = transitionsBetween(pointC, pointD);
int trDA = transitionsBetween(pointD, pointA);
// 0..3
// : :
// 1--2
int min = trAB;
ResultPoint[] points = {pointD, pointA, pointB, pointC};
if (min > trBC) {
min = trBC;
points[0] = pointA;
points[1] = pointB;
points[2] = pointC;
points[3] = pointD;
}
if (min > trCD) {
min = trCD;
points[0] = pointB;
points[1] = pointC;
points[2] = pointD;
points[3] = pointA;
}
if (min > trDA) {
points[0] = pointC;
points[1] = pointD;
points[2] = pointA;
points[3] = pointB;
}
return points;
}
/**
* Detect a second solid side next to first solid side.
*/
private ResultPoint[] detectSolid2(ResultPoint[] points) {
// A..D
// : :
// B--C
ResultPoint pointA = points[0];
ResultPoint pointB = points[1];
ResultPoint pointC = points[2];
ResultPoint pointD = points[3];
// Transition detection on the edge is not stable.
// To safely detect, shift the points to the module center.
int tr = transitionsBetween(pointA, pointD);
ResultPoint pointBs = shiftPoint(pointB, pointC, (tr + 1) * 4);
ResultPoint pointCs = shiftPoint(pointC, pointB, (tr + 1) * 4);
int trBA = transitionsBetween(pointBs, pointA);
int trCD = transitionsBetween(pointCs, pointD);
// 0..3
// | :
// 1--2
if (trBA < trCD) {
// solid sides: A-B-C
points[0] = pointA;
points[1] = pointB;
points[2] = pointC;
points[3] = pointD;
} else {
// solid sides: B-C-D
points[0] = pointB;
points[1] = pointC;
points[2] = pointD;
points[3] = pointA;
}
return points;
}
/**
* Calculates the corner position of the white top right module.
*/
private ResultPoint correctTopRight(ResultPoint[] points) {
// A..D
// | :
// B--C
ResultPoint pointA = points[0];
ResultPoint pointB = points[1];
ResultPoint pointC = points[2];
ResultPoint pointD = points[3];
// shift points for safe transition detection.
int trTop = transitionsBetween(pointA, pointD);
int trRight = transitionsBetween(pointB, pointD);
ResultPoint pointAs = shiftPoint(pointA, pointB, (trRight + 1) * 4);
ResultPoint pointCs = shiftPoint(pointC, pointB, (trTop + 1) * 4);
trTop = transitionsBetween(pointAs, pointD);
trRight = transitionsBetween(pointCs, pointD);
ResultPoint candidate1 = new ResultPoint(
pointD.getX() + (pointC.getX() - pointB.getX()) / (trTop + 1),
pointD.getY() + (pointC.getY() - pointB.getY()) / (trTop + 1));
ResultPoint candidate2 = new ResultPoint(
pointD.getX() + (pointA.getX() - pointB.getX()) / (trRight + 1),
pointD.getY() + (pointA.getY() - pointB.getY()) / (trRight + 1));
if (!isValid(candidate1)) {
if (isValid(candidate2)) {
return candidate2;
}
return null;
}
if (!isValid(candidate2)) {
return candidate1;
}
int sumc1 = transitionsBetween(pointAs, candidate1) + transitionsBetween(pointCs, candidate1);
int sumc2 = transitionsBetween(pointAs, candidate2) + transitionsBetween(pointCs, candidate2);
if (sumc1 > sumc2) {
return candidate1;
} else {
return candidate2;
}
}
/**
* Shift the edge points to the module center.
*/
private ResultPoint[] shiftToModuleCenter(ResultPoint[] points) {
// A..D
// | :
// B--C
ResultPoint pointA = points[0];
ResultPoint pointB = points[1];
ResultPoint pointC = points[2];
ResultPoint pointD = points[3];
// calculate pseudo dimensions
int dimH = transitionsBetween(pointA, pointD) + 1;
int dimV = transitionsBetween(pointC, pointD) + 1;
// shift points for safe dimension detection
ResultPoint pointAs = shiftPoint(pointA, pointB, dimV * 4);
ResultPoint pointCs = shiftPoint(pointC, pointB, dimH * 4);
// calculate more precise dimensions
dimH = transitionsBetween(pointAs, pointD) + 1;
dimV = transitionsBetween(pointCs, pointD) + 1;
if ((dimH & 0x01) == 1) {
dimH += 1;
}
if ((dimV & 0x01) == 1) {
dimV += 1;
}
// WhiteRectangleDetector returns points inside of the rectangle.
// I want points on the edges.
float centerX = (pointA.getX() + pointB.getX() + pointC.getX() + pointD.getX()) / 4;
float centerY = (pointA.getY() + pointB.getY() + pointC.getY() + pointD.getY()) / 4;
pointA = moveAway(pointA, centerX, centerY);
pointB = moveAway(pointB, centerX, centerY);
pointC = moveAway(pointC, centerX, centerY);
pointD = moveAway(pointD, centerX, centerY);
ResultPoint pointBs;
ResultPoint pointDs;
// shift points to the center of each modules
pointAs = shiftPoint(pointA, pointB, dimV * 4);
pointAs = shiftPoint(pointAs, pointD, dimH * 4);
pointBs = shiftPoint(pointB, pointA, dimV * 4);
pointBs = shiftPoint(pointBs, pointC, dimH * 4);
pointCs = shiftPoint(pointC, pointD, dimV * 4);
pointCs = shiftPoint(pointCs, pointB, dimH * 4);
pointDs = shiftPoint(pointD, pointC, dimV * 4);
pointDs = shiftPoint(pointDs, pointA, dimH * 4);
return new ResultPoint[]{pointAs, pointBs, pointCs, pointDs};
}
private boolean isValid(ResultPoint p) {
return p.getX() >= 0 && p.getX() <= image.getWidth() - 1 && p.getY() > 0 && p.getY() <= image.getHeight() - 1;
}
private static BitMatrix sampleGrid(BitMatrix image,
ResultPoint topLeft,
ResultPoint bottomLeft,
ResultPoint bottomRight,
ResultPoint topRight,
int dimensionX,
int dimensionY) throws NotFoundException {
GridSampler sampler = GridSampler.getInstance();
return sampler.sampleGrid(image,
dimensionX,
dimensionY,
0.5f,
0.5f,
dimensionX - 0.5f,
0.5f,
dimensionX - 0.5f,
dimensionY - 0.5f,
0.5f,
dimensionY - 0.5f,
topLeft.getX(),
topLeft.getY(),
topRight.getX(),
topRight.getY(),
bottomRight.getX(),
bottomRight.getY(),
bottomLeft.getX(),
bottomLeft.getY());
}
/**
* Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
*/
private int transitionsBetween(ResultPoint from, ResultPoint to) {
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
int fromX = (int) from.getX();
int fromY = (int) from.getY();
int toX = (int) to.getX();
int toY = Math.min(image.getHeight() - 1, (int) to.getY());
boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
if (steep) {
int temp = fromX;
fromX = fromY;
fromY = temp;
temp = toX;
toX = toY;
toY = temp;
}
int dx = Math.abs(toX - fromX);
int dy = Math.abs(toY - fromY);
int error = -dx / 2;
int ystep = fromY < toY ? 1 : -1;
int xstep = fromX < toX ? 1 : -1;
int transitions = 0;
boolean inBlack = image.get(steep ? fromY : fromX, steep ? fromX : fromY);
for (int x = fromX, y = fromY; x != toX; x += xstep) {
boolean isBlack = image.get(steep ? y : x, steep ? x : y);
if (isBlack != inBlack) {
transitions++;
inBlack = isBlack;
}
error += dy;
if (error > 0) {
if (y == toY) {
break;
}
y += ystep;
error -= dx;
}
}
return transitions;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
final class ASCIIEncoder implements Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.ASCII_ENCODATION;
}
@Override
public void encode(EncoderContext context) {
//step B
int n = HighLevelEncoder.determineConsecutiveDigitCount(context.getMessage(), context.pos);
if (n >= 2) {
context.writeCodeword(encodeASCIIDigits(context.getMessage().charAt(context.pos),
context.getMessage().charAt(context.pos + 1)));
context.pos += 2;
} else {
char c = context.getCurrentChar();
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
switch (newMode) {
case HighLevelEncoder.BASE256_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_BASE256);
context.signalEncoderChange(HighLevelEncoder.BASE256_ENCODATION);
return;
case HighLevelEncoder.C40_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);
context.signalEncoderChange(HighLevelEncoder.C40_ENCODATION);
return;
case HighLevelEncoder.X12_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_ANSIX12);
context.signalEncoderChange(HighLevelEncoder.X12_ENCODATION);
break;
case HighLevelEncoder.TEXT_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_TEXT);
context.signalEncoderChange(HighLevelEncoder.TEXT_ENCODATION);
break;
case HighLevelEncoder.EDIFACT_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_EDIFACT);
context.signalEncoderChange(HighLevelEncoder.EDIFACT_ENCODATION);
break;
default:
throw new IllegalStateException("Illegal mode: " + newMode);
}
} else if (HighLevelEncoder.isExtendedASCII(c)) {
context.writeCodeword(HighLevelEncoder.UPPER_SHIFT);
context.writeCodeword((char) (c - 128 + 1));
context.pos++;
} else {
context.writeCodeword((char) (c + 1));
context.pos++;
}
}
}
private static char encodeASCIIDigits(char digit1, char digit2) {
if (HighLevelEncoder.isDigit(digit1) && HighLevelEncoder.isDigit(digit2)) {
int num = (digit1 - 48) * 10 + (digit2 - 48);
return (char) (num + 130);
}
throw new IllegalArgumentException("not digits: " + digit1 + digit2);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
final class Base256Encoder implements Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.BASE256_ENCODATION;
}
@Override
public void encode(EncoderContext context) {
StringBuilder buffer = new StringBuilder();
buffer.append('\0'); //Initialize length field
while (context.hasMoreCharacters()) {
char c = context.getCurrentChar();
buffer.append(c);
context.pos++;
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
// Return to ASCII encodation, which will actually handle latch to new mode
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
break;
}
}
int dataCount = buffer.length() - 1;
int lengthFieldSize = 1;
int currentSize = context.getCodewordCount() + dataCount + lengthFieldSize;
context.updateSymbolInfo(currentSize);
boolean mustPad = (context.getSymbolInfo().getDataCapacity() - currentSize) > 0;
if (context.hasMoreCharacters() || mustPad) {
if (dataCount <= 249) {
buffer.setCharAt(0, (char) dataCount);
} else if (dataCount <= 1555) {
buffer.setCharAt(0, (char) ((dataCount / 250) + 249));
buffer.insert(1, (char) (dataCount % 250));
} else {
throw new IllegalStateException(
"Message length not in valid ranges: " + dataCount);
}
}
for (int i = 0, c = buffer.length(); i < c; i++) {
context.writeCodeword(randomize255State(
buffer.charAt(i), context.getCodewordCount() + 1));
}
}
private static char randomize255State(char ch, int codewordPosition) {
int pseudoRandom = ((149 * codewordPosition) % 255) + 1;
int tempVariable = ch + pseudoRandom;
if (tempVariable <= 255) {
return (char) tempVariable;
} else {
return (char) (tempVariable - 256);
}
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
class C40Encoder implements Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.C40_ENCODATION;
}
void encodeMaximal(EncoderContext context) {
StringBuilder buffer = new StringBuilder();
int lastCharSize = 0;
int backtrackStartPosition = context.pos;
int backtrackBufferLength = 0;
while (context.hasMoreCharacters()) {
char c = context.getCurrentChar();
context.pos++;
lastCharSize = encodeChar(c, buffer);
if (buffer.length() % 3 == 0) {
backtrackStartPosition = context.pos;
backtrackBufferLength = buffer.length();
}
}
if (backtrackBufferLength != buffer.length()) {
int unwritten = (buffer.length() / 3) * 2;
int curCodewordCount = context.getCodewordCount() + unwritten + 1; // +1 for the latch to C40
context.updateSymbolInfo(curCodewordCount);
int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
int rest = buffer.length() % 3;
if ((rest == 2 && available != 2) ||
(rest == 1 && (lastCharSize > 3 || available != 1))) {
buffer.setLength(backtrackBufferLength);
context.pos = backtrackStartPosition;
}
}
if (buffer.length() > 0) {
context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);
}
handleEOD(context, buffer);
}
@Override
public void encode(EncoderContext context) {
//step C
StringBuilder buffer = new StringBuilder();
while (context.hasMoreCharacters()) {
char c = context.getCurrentChar();
context.pos++;
int lastCharSize = encodeChar(c, buffer);
int unwritten = (buffer.length() / 3) * 2;
int curCodewordCount = context.getCodewordCount() + unwritten;
context.updateSymbolInfo(curCodewordCount);
int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
if (!context.hasMoreCharacters()) {
//Avoid having a single C40 value in the last triplet
StringBuilder removed = new StringBuilder();
if ((buffer.length() % 3) == 2 && available != 2) {
lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
}
while ((buffer.length() % 3) == 1 && (lastCharSize > 3 || available != 1)) {
lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
}
break;
}
int count = buffer.length();
if ((count % 3) == 0) {
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
// Return to ASCII encodation, which will actually handle latch to new mode
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
break;
}
}
}
handleEOD(context, buffer);
}
private int backtrackOneCharacter(EncoderContext context,
StringBuilder buffer, StringBuilder removed, int lastCharSize) {
int count = buffer.length();
buffer.delete(count - lastCharSize, count);
context.pos--;
char c = context.getCurrentChar();
lastCharSize = encodeChar(c, removed);
context.resetSymbolInfo(); //Deal with possible reduction in symbol size
return lastCharSize;
}
static void writeNextTriplet(EncoderContext context, StringBuilder buffer) {
context.writeCodewords(encodeToCodewords(buffer));
buffer.delete(0, 3);
}
/**
* Handle "end of data" situations
*
* @param context the encoder context
* @param buffer the buffer with the remaining encoded characters
*/
void handleEOD(EncoderContext context, StringBuilder buffer) {
int unwritten = (buffer.length() / 3) * 2;
int rest = buffer.length() % 3;
int curCodewordCount = context.getCodewordCount() + unwritten;
context.updateSymbolInfo(curCodewordCount);
int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
if (rest == 2) {
buffer.append('\0'); //Shift 1
while (buffer.length() >= 3) {
writeNextTriplet(context, buffer);
}
if (context.hasMoreCharacters()) {
context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
}
} else if (available == 1 && rest == 1) {
while (buffer.length() >= 3) {
writeNextTriplet(context, buffer);
}
if (context.hasMoreCharacters()) {
context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
}
// else no unlatch
context.pos--;
} else if (rest == 0) {
while (buffer.length() >= 3) {
writeNextTriplet(context, buffer);
}
if (available > 0 || context.hasMoreCharacters()) {
context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
}
} else {
throw new IllegalStateException("Unexpected case. Please report!");
}
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
}
int encodeChar(char c, StringBuilder sb) {
if (c == ' ') {
sb.append('\3');
return 1;
}
if (c >= '0' && c <= '9') {
sb.append((char) (c - 48 + 4));
return 1;
}
if (c >= 'A' && c <= 'Z') {
sb.append((char) (c - 65 + 14));
return 1;
}
if (c < ' ') {
sb.append('\0'); //Shift 1 Set
sb.append(c);
return 2;
}
if (c <= '/') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 33));
return 2;
}
if (c <= '@') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 58 + 15));
return 2;
}
if (c <= '_') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 91 + 22));
return 2;
}
if (c <= 127) {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 96));
return 2;
}
sb.append("\1\u001e"); //Shift 2, Upper Shift
int len = 2;
len += encodeChar((char) (c - 128), sb);
return len;
}
private static String encodeToCodewords(CharSequence sb) {
int v = (1600 * sb.charAt(0)) + (40 * sb.charAt(1)) + sb.charAt(2) + 1;
char cw1 = (char) (v / 256);
char cw2 = (char) (v % 256);
return new String(new char[] {cw1, cw2});
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2006 Jeremias Maerki
*
* 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.encoder;
final class DataMatrixSymbolInfo144 extends SymbolInfo {
DataMatrixSymbolInfo144() {
super(false, 1558, 620, 22, 22, 36, -1, 62);
}
@Override
public int getInterleavedBlockCount() {
return 10;
}
@Override
public int getDataLengthForInterleavedBlock(int index) {
return (index <= 8) ? 156 : 155;
}
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright 2006 Jeremias Maerki.
*
* 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.encoder;
import java.util.Arrays;
/**
* Symbol Character Placement Program. Adapted from Annex M.1 in ISO/IEC 16022:2000(E).
*/
public class DefaultPlacement {
private final CharSequence codewords;
private final int numrows;
private final int numcols;
private final byte[] bits;
/**
* Main constructor
*
* @param codewords the codewords to place
* @param numcols the number of columns
* @param numrows the number of rows
*/
public DefaultPlacement(CharSequence codewords, int numcols, int numrows) {
this.codewords = codewords;
this.numcols = numcols;
this.numrows = numrows;
this.bits = new byte[numcols * numrows];
Arrays.fill(this.bits, (byte) -1); //Initialize with "not set" value
}
final int getNumrows() {
return numrows;
}
final int getNumcols() {
return numcols;
}
final byte[] getBits() {
return bits;
}
public final boolean getBit(int col, int row) {
return bits[row * numcols + col] == 1;
}
private void setBit(int col, int row, boolean bit) {
bits[row * numcols + col] = (byte) (bit ? 1 : 0);
}
private boolean noBit(int col, int row) {
return bits[row * numcols + col] < 0;
}
public final void place() {
int pos = 0;
int row = 4;
int col = 0;
do {
// repeatedly first check for one of the special corner cases, then...
if ((row == numrows) && (col == 0)) {
corner1(pos++);
}
if ((row == numrows - 2) && (col == 0) && ((numcols % 4) != 0)) {
corner2(pos++);
}
if ((row == numrows - 2) && (col == 0) && (numcols % 8 == 4)) {
corner3(pos++);
}
if ((row == numrows + 4) && (col == 2) && ((numcols % 8) == 0)) {
corner4(pos++);
}
// sweep upward diagonally, inserting successive characters...
do {
if ((row < numrows) && (col >= 0) && noBit(col, row)) {
utah(row, col, pos++);
}
row -= 2;
col += 2;
} while (row >= 0 && (col < numcols));
row++;
col += 3;
// and then sweep downward diagonally, inserting successive characters, ...
do {
if ((row >= 0) && (col < numcols) && noBit(col, row)) {
utah(row, col, pos++);
}
row += 2;
col -= 2;
} while ((row < numrows) && (col >= 0));
row += 3;
col++;
// ...until the entire array is scanned
} while ((row < numrows) || (col < numcols));
// Lastly, if the lower right-hand corner is untouched, fill in fixed pattern
if (noBit(numcols - 1, numrows - 1)) {
setBit(numcols - 1, numrows - 1, true);
setBit(numcols - 2, numrows - 2, true);
}
}
private void module(int row, int col, int pos, int bit) {
if (row < 0) {
row += numrows;
col += 4 - ((numrows + 4) % 8);
}
if (col < 0) {
col += numcols;
row += 4 - ((numcols + 4) % 8);
}
// Note the conversion:
int v = codewords.charAt(pos);
v &= 1 << (8 - bit);
setBit(col, row, v != 0);
}
/**
* Places the 8 bits of a utah-shaped symbol character in ECC200.
*
* @param row the row
* @param col the column
* @param pos character position
*/
private void utah(int row, int col, int pos) {
module(row - 2, col - 2, pos, 1);
module(row - 2, col - 1, pos, 2);
module(row - 1, col - 2, pos, 3);
module(row - 1, col - 1, pos, 4);
module(row - 1, col, pos, 5);
module(row, col - 2, pos, 6);
module(row, col - 1, pos, 7);
module(row, col, pos, 8);
}
private void corner1(int pos) {
module(numrows - 1, 0, pos, 1);
module(numrows - 1, 1, pos, 2);
module(numrows - 1, 2, pos, 3);
module(0, numcols - 2, pos, 4);
module(0, numcols - 1, pos, 5);
module(1, numcols - 1, pos, 6);
module(2, numcols - 1, pos, 7);
module(3, numcols - 1, pos, 8);
}
private void corner2(int pos) {
module(numrows - 3, 0, pos, 1);
module(numrows - 2, 0, pos, 2);
module(numrows - 1, 0, pos, 3);
module(0, numcols - 4, pos, 4);
module(0, numcols - 3, pos, 5);
module(0, numcols - 2, pos, 6);
module(0, numcols - 1, pos, 7);
module(1, numcols - 1, pos, 8);
}
private void corner3(int pos) {
module(numrows - 3, 0, pos, 1);
module(numrows - 2, 0, pos, 2);
module(numrows - 1, 0, pos, 3);
module(0, numcols - 2, pos, 4);
module(0, numcols - 1, pos, 5);
module(1, numcols - 1, pos, 6);
module(2, numcols - 1, pos, 7);
module(3, numcols - 1, pos, 8);
}
private void corner4(int pos) {
module(numrows - 1, 0, pos, 1);
module(numrows - 1, numcols - 1, pos, 2);
module(0, numcols - 3, pos, 3);
module(0, numcols - 2, pos, 4);
module(0, numcols - 1, pos, 5);
module(1, numcols - 3, pos, 6);
module(1, numcols - 2, pos, 7);
module(1, numcols - 1, pos, 8);
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
final class EdifactEncoder implements Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.EDIFACT_ENCODATION;
}
@Override
public void encode(EncoderContext context) {
//step F
StringBuilder buffer = new StringBuilder();
while (context.hasMoreCharacters()) {
char c = context.getCurrentChar();
encodeChar(c, buffer);
context.pos++;
int count = buffer.length();
if (count >= 4) {
context.writeCodewords(encodeToCodewords(buffer));
buffer.delete(0, 4);
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
// Return to ASCII encodation, which will actually handle latch to new mode
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
break;
}
}
}
buffer.append((char) 31); //Unlatch
handleEOD(context, buffer);
}
/**
* Handle "end of data" situations
*
* @param context the encoder context
* @param buffer the buffer with the remaining encoded characters
*/
private static void handleEOD(EncoderContext context, CharSequence buffer) {
try {
int count = buffer.length();
if (count == 0) {
return; //Already finished
}
if (count == 1) {
//Only an unlatch at the end
context.updateSymbolInfo();
int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();
int remaining = context.getRemainingCharacters();
// The following two lines are a hack inspired by the 'fix' from https://sourceforge.net/p/barcode4j/svn/221/
if (remaining > available) {
context.updateSymbolInfo(context.getCodewordCount() + 1);
available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();
}
if (remaining <= available && available <= 2) {
return; //No unlatch
}
}
if (count > 4) {
throw new IllegalStateException("Count must not exceed 4");
}
int restChars = count - 1;
String encoded = encodeToCodewords(buffer);
boolean endOfSymbolReached = !context.hasMoreCharacters();
boolean restInAscii = endOfSymbolReached && restChars <= 2;
if (restChars <= 2) {
context.updateSymbolInfo(context.getCodewordCount() + restChars);
int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();
if (available >= 3) {
restInAscii = false;
context.updateSymbolInfo(context.getCodewordCount() + encoded.length());
//available = context.symbolInfo.dataCapacity - context.getCodewordCount();
}
}
if (restInAscii) {
context.resetSymbolInfo();
context.pos -= restChars;
} else {
context.writeCodewords(encoded);
}
} finally {
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
}
}
private static void encodeChar(char c, StringBuilder sb) {
if (c >= ' ' && c <= '?') {
sb.append(c);
} else if (c >= '@' && c <= '^') {
sb.append((char) (c - 64));
} else {
HighLevelEncoder.illegalCharacter(c);
}
}
private static String encodeToCodewords(CharSequence sb) {
int len = sb.length();
if (len == 0) {
throw new IllegalStateException("StringBuilder must not be empty");
}
char c1 = sb.charAt(0);
char c2 = len >= 2 ? sb.charAt(1) : 0;
char c3 = len >= 3 ? sb.charAt(2) : 0;
char c4 = len >= 4 ? sb.charAt(3) : 0;
int v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;
char cw1 = (char) ((v >> 16) & 255);
char cw2 = (char) ((v >> 8) & 255);
char cw3 = (char) (v & 255);
StringBuilder res = new StringBuilder(3);
res.append(cw1);
if (len >= 2) {
res.append(cw2);
}
if (len >= 3) {
res.append(cw3);
}
return res.toString();
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
interface Encoder {
int getEncodingMode();
void encode(EncoderContext context);
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
import com.google.zxing.Dimension;
import java.nio.charset.StandardCharsets;
final class EncoderContext {
private final String msg;
private SymbolShapeHint shape;
private Dimension minSize;
private Dimension maxSize;
private final StringBuilder codewords;
int pos;
private int newEncoding;
private SymbolInfo symbolInfo;
private int skipAtEnd;
EncoderContext(String msg) {
//From this point on Strings are not Unicode anymore!
byte[] msgBinary = msg.getBytes(StandardCharsets.ISO_8859_1);
StringBuilder sb = new StringBuilder(msgBinary.length);
for (int i = 0, c = msgBinary.length; i < c; i++) {
char ch = (char) (msgBinary[i] & 0xff);
if (ch == '?' && msg.charAt(i) != '?') {
throw new IllegalArgumentException("Message contains characters outside ISO-8859-1 encoding.");
}
sb.append(ch);
}
this.msg = sb.toString(); //Not Unicode here!
shape = SymbolShapeHint.FORCE_NONE;
this.codewords = new StringBuilder(msg.length());
newEncoding = -1;
}
public void setSymbolShape(SymbolShapeHint shape) {
this.shape = shape;
}
public void setSizeConstraints(Dimension minSize, Dimension maxSize) {
this.minSize = minSize;
this.maxSize = maxSize;
}
public String getMessage() {
return this.msg;
}
public void setSkipAtEnd(int count) {
this.skipAtEnd = count;
}
public char getCurrentChar() {
return msg.charAt(pos);
}
public char getCurrent() {
return msg.charAt(pos);
}
public StringBuilder getCodewords() {
return codewords;
}
public void writeCodewords(String codewords) {
this.codewords.append(codewords);
}
public void writeCodeword(char codeword) {
this.codewords.append(codeword);
}
public int getCodewordCount() {
return this.codewords.length();
}
public int getNewEncoding() {
return newEncoding;
}
public void signalEncoderChange(int encoding) {
this.newEncoding = encoding;
}
public void resetEncoderSignal() {
this.newEncoding = -1;
}
public boolean hasMoreCharacters() {
return pos < getTotalMessageCharCount();
}
private int getTotalMessageCharCount() {
return msg.length() - skipAtEnd;
}
public int getRemainingCharacters() {
return getTotalMessageCharCount() - pos;
}
public SymbolInfo getSymbolInfo() {
return symbolInfo;
}
public void updateSymbolInfo() {
updateSymbolInfo(getCodewordCount());
}
public void updateSymbolInfo(int len) {
if (this.symbolInfo == null || len > this.symbolInfo.getDataCapacity()) {
this.symbolInfo = SymbolInfo.lookup(len, shape, minSize, maxSize, true);
}
}
public void resetSymbolInfo() {
this.symbolInfo = null;
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2006 Jeremias Maerki.
*
* 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.encoder;
/**
* Error Correction Code for ECC200.
*/
public final class ErrorCorrection {
/**
* Lookup table which factors to use for which number of error correction codewords.
* See FACTORS.
*/
private static final int[] FACTOR_SETS
= {5, 7, 10, 11, 12, 14, 18, 20, 24, 28, 36, 42, 48, 56, 62, 68};
/**
* Precomputed polynomial factors for ECC 200.
*/
private static final int[][] FACTORS = {
{228, 48, 15, 111, 62},
{23, 68, 144, 134, 240, 92, 254},
{28, 24, 185, 166, 223, 248, 116, 255, 110, 61},
{175, 138, 205, 12, 194, 168, 39, 245, 60, 97, 120},
{41, 153, 158, 91, 61, 42, 142, 213, 97, 178, 100, 242},
{156, 97, 192, 252, 95, 9, 157, 119, 138, 45, 18, 186, 83, 185},
{83, 195, 100, 39, 188, 75, 66, 61, 241, 213, 109, 129, 94, 254, 225, 48, 90, 188},
{15, 195, 244, 9, 233, 71, 168, 2, 188, 160, 153, 145, 253, 79, 108, 82, 27, 174, 186, 172},
{52, 190, 88, 205, 109, 39, 176, 21, 155, 197, 251, 223, 155, 21, 5, 172,
254, 124, 12, 181, 184, 96, 50, 193},
{211, 231, 43, 97, 71, 96, 103, 174, 37, 151, 170, 53, 75, 34, 249, 121,
17, 138, 110, 213, 141, 136, 120, 151, 233, 168, 93, 255},
{245, 127, 242, 218, 130, 250, 162, 181, 102, 120, 84, 179, 220, 251, 80, 182,
229, 18, 2, 4, 68, 33, 101, 137, 95, 119, 115, 44, 175, 184, 59, 25,
225, 98, 81, 112},
{77, 193, 137, 31, 19, 38, 22, 153, 247, 105, 122, 2, 245, 133, 242, 8,
175, 95, 100, 9, 167, 105, 214, 111, 57, 121, 21, 1, 253, 57, 54, 101,
248, 202, 69, 50, 150, 177, 226, 5, 9, 5},
{245, 132, 172, 223, 96, 32, 117, 22, 238, 133, 238, 231, 205, 188, 237, 87,
191, 106, 16, 147, 118, 23, 37, 90, 170, 205, 131, 88, 120, 100, 66, 138,
186, 240, 82, 44, 176, 87, 187, 147, 160, 175, 69, 213, 92, 253, 225, 19},
{175, 9, 223, 238, 12, 17, 220, 208, 100, 29, 175, 170, 230, 192, 215, 235,
150, 159, 36, 223, 38, 200, 132, 54, 228, 146, 218, 234, 117, 203, 29, 232,
144, 238, 22, 150, 201, 117, 62, 207, 164, 13, 137, 245, 127, 67, 247, 28,
155, 43, 203, 107, 233, 53, 143, 46},
{242, 93, 169, 50, 144, 210, 39, 118, 202, 188, 201, 189, 143, 108, 196, 37,
185, 112, 134, 230, 245, 63, 197, 190, 250, 106, 185, 221, 175, 64, 114, 71,
161, 44, 147, 6, 27, 218, 51, 63, 87, 10, 40, 130, 188, 17, 163, 31,
176, 170, 4, 107, 232, 7, 94, 166, 224, 124, 86, 47, 11, 204},
{220, 228, 173, 89, 251, 149, 159, 56, 89, 33, 147, 244, 154, 36, 73, 127,
213, 136, 248, 180, 234, 197, 158, 177, 68, 122, 93, 213, 15, 160, 227, 236,
66, 139, 153, 185, 202, 167, 179, 25, 220, 232, 96, 210, 231, 136, 223, 239,
181, 241, 59, 52, 172, 25, 49, 232, 211, 189, 64, 54, 108, 153, 132, 63,
96, 103, 82, 186}};
private static final int MODULO_VALUE = 0x12D;
private static final int[] LOG;
private static final int[] ALOG;
static {
//Create log and antilog table
LOG = new int[256];
ALOG = new int[255];
int p = 1;
for (int i = 0; i < 255; i++) {
ALOG[i] = p;
LOG[p] = i;
p *= 2;
if (p >= 256) {
p ^= MODULO_VALUE;
}
}
}
private ErrorCorrection() {
}
/**
* Creates the ECC200 error correction for an encoded message.
*
* @param codewords the codewords
* @param symbolInfo information about the symbol to be encoded
* @return the codewords with interleaved error correction.
*/
public static String encodeECC200(String codewords, SymbolInfo symbolInfo) {
if (codewords.length() != symbolInfo.getDataCapacity()) {
throw new IllegalArgumentException(
"The number of codewords does not match the selected symbol");
}
StringBuilder sb = new StringBuilder(symbolInfo.getDataCapacity() + symbolInfo.getErrorCodewords());
sb.append(codewords);
int blockCount = symbolInfo.getInterleavedBlockCount();
if (blockCount == 1) {
String ecc = createECCBlock(codewords, symbolInfo.getErrorCodewords());
sb.append(ecc);
} else {
sb.setLength(sb.capacity());
int[] dataSizes = new int[blockCount];
int[] errorSizes = new int[blockCount];
for (int i = 0; i < blockCount; i++) {
dataSizes[i] = symbolInfo.getDataLengthForInterleavedBlock(i + 1);
errorSizes[i] = symbolInfo.getErrorLengthForInterleavedBlock(i + 1);
}
for (int block = 0; block < blockCount; block++) {
StringBuilder temp = new StringBuilder(dataSizes[block]);
for (int d = block; d < symbolInfo.getDataCapacity(); d += blockCount) {
temp.append(codewords.charAt(d));
}
String ecc = createECCBlock(temp.toString(), errorSizes[block]);
int pos = 0;
for (int e = block; e < errorSizes[block] * blockCount; e += blockCount) {
sb.setCharAt(symbolInfo.getDataCapacity() + e, ecc.charAt(pos++));
}
}
}
return sb.toString();
}
private static String createECCBlock(CharSequence codewords, int numECWords) {
int table = -1;
for (int i = 0; i < FACTOR_SETS.length; i++) {
if (FACTOR_SETS[i] == numECWords) {
table = i;
break;
}
}
if (table < 0) {
throw new IllegalArgumentException(
"Illegal number of error correction codewords specified: " + numECWords);
}
int[] poly = FACTORS[table];
char[] ecc = new char[numECWords];
for (int i = 0; i < numECWords; i++) {
ecc[i] = 0;
}
for (int i = 0; i < codewords.length(); i++) {
int m = ecc[numECWords - 1] ^ codewords.charAt(i);
for (int k = numECWords - 1; k > 0; k--) {
if (m != 0 && poly[k] != 0) {
ecc[k] = (char) (ecc[k - 1] ^ ALOG[(LOG[m] + LOG[poly[k]]) % 255]);
} else {
ecc[k] = ecc[k - 1];
}
}
if (m != 0 && poly[0] != 0) {
ecc[0] = (char) ALOG[(LOG[m] + LOG[poly[0]]) % 255];
} else {
ecc[0] = 0;
}
}
char[] eccReversed = new char[numECWords];
for (int i = 0; i < numECWords; i++) {
eccReversed[i] = ecc[numECWords - i - 1];
}
return String.valueOf(eccReversed);
}
}

View File

@@ -0,0 +1,484 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
import com.google.zxing.Dimension;
import java.util.Arrays;
/**
* DataMatrix ECC 200 data encoder following the algorithm described in ISO/IEC 16022:200(E) in
* annex S.
*/
public final class HighLevelEncoder {
/**
* Padding character
*/
private static final char PAD = 129;
/**
* mode latch to C40 encodation mode
*/
static final char LATCH_TO_C40 = 230;
/**
* mode latch to Base 256 encodation mode
*/
static final char LATCH_TO_BASE256 = 231;
/**
* FNC1 Codeword
*/
//private static final char FNC1 = 232;
/**
* Structured Append Codeword
*/
//private static final char STRUCTURED_APPEND = 233;
/**
* Reader Programming
*/
//private static final char READER_PROGRAMMING = 234;
/**
* Upper Shift
*/
static final char UPPER_SHIFT = 235;
/**
* 05 Macro
*/
private static final char MACRO_05 = 236;
/**
* 06 Macro
*/
private static final char MACRO_06 = 237;
/**
* mode latch to ANSI X.12 encodation mode
*/
static final char LATCH_TO_ANSIX12 = 238;
/**
* mode latch to Text encodation mode
*/
static final char LATCH_TO_TEXT = 239;
/**
* mode latch to EDIFACT encodation mode
*/
static final char LATCH_TO_EDIFACT = 240;
/**
* ECI character (Extended Channel Interpretation)
*/
//private static final char ECI = 241;
/**
* Unlatch from C40 encodation
*/
static final char C40_UNLATCH = 254;
/**
* Unlatch from X12 encodation
*/
static final char X12_UNLATCH = 254;
/**
* 05 Macro header
*/
static final String MACRO_05_HEADER = "[)>\u001E05\u001D";
/**
* 06 Macro header
*/
static final String MACRO_06_HEADER = "[)>\u001E06\u001D";
/**
* Macro trailer
*/
static final String MACRO_TRAILER = "\u001E\u0004";
static final int ASCII_ENCODATION = 0;
static final int C40_ENCODATION = 1;
static final int TEXT_ENCODATION = 2;
static final int X12_ENCODATION = 3;
static final int EDIFACT_ENCODATION = 4;
static final int BASE256_ENCODATION = 5;
private HighLevelEncoder() {
}
private static char randomize253State(int codewordPosition) {
int pseudoRandom = ((149 * codewordPosition) % 253) + 1;
int tempVariable = PAD + pseudoRandom;
return (char) (tempVariable <= 254 ? tempVariable : tempVariable - 254);
}
/**
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
* of ISO/IEC 16022:2000(E).
*
* @param msg the message
* @return the encoded message (the char values range from 0 to 255)
*/
public static String encodeHighLevel(String msg) {
return encodeHighLevel(msg, SymbolShapeHint.FORCE_NONE, null, null, false);
}
/**
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
* of ISO/IEC 16022:2000(E).
*
* @param msg the message
* @param shape requested shape. May be {@code SymbolShapeHint.FORCE_NONE},
* {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.
* @param minSize the minimum symbol size constraint or null for no constraint
* @param maxSize the maximum symbol size constraint or null for no constraint
* @return the encoded message (the char values range from 0 to 255)
*/
public static String encodeHighLevel(String msg,
SymbolShapeHint shape,
Dimension minSize,
Dimension maxSize) {
return encodeHighLevel(msg, shape, minSize, maxSize, false);
}
/**
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
* of ISO/IEC 16022:2000(E).
*
* @param msg the message
* @param shape requested shape. May be {@code SymbolShapeHint.FORCE_NONE},
* {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.
* @param minSize the minimum symbol size constraint or null for no constraint
* @param maxSize the maximum symbol size constraint or null for no constraint
* @param forceC40 enforce C40 encoding
* @return the encoded message (the char values range from 0 to 255)
*/
public static String encodeHighLevel(String msg,
SymbolShapeHint shape,
Dimension minSize,
Dimension maxSize,
boolean forceC40) {
//the codewords 0..255 are encoded as Unicode characters
C40Encoder c40Encoder = new C40Encoder();
Encoder[] encoders = {
new ASCIIEncoder(), c40Encoder, new TextEncoder(),
new X12Encoder(), new EdifactEncoder(), new Base256Encoder()
};
EncoderContext context = new EncoderContext(msg);
context.setSymbolShape(shape);
context.setSizeConstraints(minSize, maxSize);
if (msg.startsWith(MACRO_05_HEADER) && msg.endsWith(MACRO_TRAILER)) {
context.writeCodeword(MACRO_05);
context.setSkipAtEnd(2);
context.pos += MACRO_05_HEADER.length();
} else if (msg.startsWith(MACRO_06_HEADER) && msg.endsWith(MACRO_TRAILER)) {
context.writeCodeword(MACRO_06);
context.setSkipAtEnd(2);
context.pos += MACRO_06_HEADER.length();
}
int encodingMode = ASCII_ENCODATION; //Default mode
if (forceC40) {
c40Encoder.encodeMaximal(context);
encodingMode = context.getNewEncoding();
context.resetEncoderSignal();
}
while (context.hasMoreCharacters()) {
encoders[encodingMode].encode(context);
if (context.getNewEncoding() >= 0) {
encodingMode = context.getNewEncoding();
context.resetEncoderSignal();
}
}
int len = context.getCodewordCount();
context.updateSymbolInfo();
int capacity = context.getSymbolInfo().getDataCapacity();
if (len < capacity &&
encodingMode != ASCII_ENCODATION &&
encodingMode != BASE256_ENCODATION &&
encodingMode != EDIFACT_ENCODATION) {
context.writeCodeword('\u00fe'); //Unlatch (254)
}
//Padding
StringBuilder codewords = context.getCodewords();
if (codewords.length() < capacity) {
codewords.append(PAD);
}
while (codewords.length() < capacity) {
codewords.append(randomize253State(codewords.length() + 1));
}
return context.getCodewords().toString();
}
static int lookAheadTest(CharSequence msg, int startpos, int currentMode) {
int newMode = lookAheadTestIntern(msg, startpos, currentMode);
if (currentMode == X12_ENCODATION && newMode == X12_ENCODATION) {
int endpos = Math.min(startpos + 3, msg.length());
for (int i = startpos; i < endpos; i++) {
if (!isNativeX12(msg.charAt(i))) {
return ASCII_ENCODATION;
}
}
} else if (currentMode == EDIFACT_ENCODATION && newMode == EDIFACT_ENCODATION) {
int endpos = Math.min(startpos + 4, msg.length());
for (int i = startpos; i < endpos; i++) {
if (!isNativeEDIFACT(msg.charAt(i))) {
return ASCII_ENCODATION;
}
}
}
return newMode;
}
static int lookAheadTestIntern(CharSequence msg, int startpos, int currentMode) {
if (startpos >= msg.length()) {
return currentMode;
}
float[] charCounts;
//step J
if (currentMode == ASCII_ENCODATION) {
charCounts = new float[]{0, 1, 1, 1, 1, 1.25f};
} else {
charCounts = new float[]{1, 2, 2, 2, 2, 2.25f};
charCounts[currentMode] = 0;
}
int charsProcessed = 0;
byte[] mins = new byte[6];
int[] intCharCounts = new int[6];
while (true) {
//step K
if ((startpos + charsProcessed) == msg.length()) {
Arrays.fill(mins, (byte) 0);
Arrays.fill(intCharCounts, 0);
int min = findMinimums(charCounts, intCharCounts, Integer.MAX_VALUE, mins);
int minCount = getMinimumCount(mins);
if (intCharCounts[ASCII_ENCODATION] == min) {
return ASCII_ENCODATION;
}
if (minCount == 1) {
if (mins[BASE256_ENCODATION] > 0) {
return BASE256_ENCODATION;
}
if (mins[EDIFACT_ENCODATION] > 0) {
return EDIFACT_ENCODATION;
}
if (mins[TEXT_ENCODATION] > 0) {
return TEXT_ENCODATION;
}
if (mins[X12_ENCODATION] > 0) {
return X12_ENCODATION;
}
}
return C40_ENCODATION;
}
char c = msg.charAt(startpos + charsProcessed);
charsProcessed++;
//step L
if (isDigit(c)) {
charCounts[ASCII_ENCODATION] += 0.5f;
} else if (isExtendedASCII(c)) {
charCounts[ASCII_ENCODATION] = (float) Math.ceil(charCounts[ASCII_ENCODATION]);
charCounts[ASCII_ENCODATION] += 2.0f;
} else {
charCounts[ASCII_ENCODATION] = (float) Math.ceil(charCounts[ASCII_ENCODATION]);
charCounts[ASCII_ENCODATION]++;
}
//step M
if (isNativeC40(c)) {
charCounts[C40_ENCODATION] += 2.0f / 3.0f;
} else if (isExtendedASCII(c)) {
charCounts[C40_ENCODATION] += 8.0f / 3.0f;
} else {
charCounts[C40_ENCODATION] += 4.0f / 3.0f;
}
//step N
if (isNativeText(c)) {
charCounts[TEXT_ENCODATION] += 2.0f / 3.0f;
} else if (isExtendedASCII(c)) {
charCounts[TEXT_ENCODATION] += 8.0f / 3.0f;
} else {
charCounts[TEXT_ENCODATION] += 4.0f / 3.0f;
}
//step O
if (isNativeX12(c)) {
charCounts[X12_ENCODATION] += 2.0f / 3.0f;
} else if (isExtendedASCII(c)) {
charCounts[X12_ENCODATION] += 13.0f / 3.0f;
} else {
charCounts[X12_ENCODATION] += 10.0f / 3.0f;
}
//step P
if (isNativeEDIFACT(c)) {
charCounts[EDIFACT_ENCODATION] += 3.0f / 4.0f;
} else if (isExtendedASCII(c)) {
charCounts[EDIFACT_ENCODATION] += 17.0f / 4.0f;
} else {
charCounts[EDIFACT_ENCODATION] += 13.0f / 4.0f;
}
// step Q
if (isSpecialB256(c)) {
charCounts[BASE256_ENCODATION] += 4.0f;
} else {
charCounts[BASE256_ENCODATION]++;
}
//step R
if (charsProcessed >= 4) {
Arrays.fill(mins, (byte) 0);
Arrays.fill(intCharCounts, 0);
findMinimums(charCounts, intCharCounts, Integer.MAX_VALUE, mins);
if (intCharCounts[ASCII_ENCODATION] < min(intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION], intCharCounts[TEXT_ENCODATION], intCharCounts[X12_ENCODATION],
intCharCounts[EDIFACT_ENCODATION])) {
return ASCII_ENCODATION;
}
if (intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION] ||
intCharCounts[BASE256_ENCODATION] + 1 < min(intCharCounts[C40_ENCODATION],
intCharCounts[TEXT_ENCODATION], intCharCounts[X12_ENCODATION], intCharCounts[EDIFACT_ENCODATION])) {
return BASE256_ENCODATION;
}
if (intCharCounts[EDIFACT_ENCODATION] + 1 < min(intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION] , intCharCounts[TEXT_ENCODATION] , intCharCounts[X12_ENCODATION],
intCharCounts[ASCII_ENCODATION])) {
return EDIFACT_ENCODATION;
}
if (intCharCounts[TEXT_ENCODATION] + 1 < min(intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION] , intCharCounts[EDIFACT_ENCODATION] , intCharCounts[X12_ENCODATION],
intCharCounts[ASCII_ENCODATION])) {
return TEXT_ENCODATION;
}
if (intCharCounts[X12_ENCODATION] + 1 < min(intCharCounts[BASE256_ENCODATION],
intCharCounts[C40_ENCODATION] , intCharCounts[EDIFACT_ENCODATION] , intCharCounts[TEXT_ENCODATION],
intCharCounts[ASCII_ENCODATION])) {
return X12_ENCODATION;
}
if (intCharCounts[C40_ENCODATION] + 1 < min(intCharCounts[ASCII_ENCODATION],
intCharCounts[BASE256_ENCODATION] , intCharCounts[EDIFACT_ENCODATION] , intCharCounts[TEXT_ENCODATION])) {
if (intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION]) {
return C40_ENCODATION;
}
if (intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION]) {
int p = startpos + charsProcessed + 1;
while (p < msg.length()) {
char tc = msg.charAt(p);
if (isX12TermSep(tc)) {
return X12_ENCODATION;
}
if (!isNativeX12(tc)) {
break;
}
p++;
}
return C40_ENCODATION;
}
}
}
}
}
private static int min(int f1, int f2, int f3, int f4, int f5) {
return Math.min(min(f1, f2, f3, f4),f5);
}
private static int min(int f1, int f2, int f3, int f4) {
return Math.min(f1, Math.min(f2, Math.min(f3, f4)));
}
private static int findMinimums(float[] charCounts, int[] intCharCounts, int min, byte[] mins) {
for (int i = 0; i < 6; i++) {
int current = (intCharCounts[i] = (int) Math.ceil(charCounts[i]));
if (min > current) {
min = current;
Arrays.fill(mins, (byte) 0);
}
if (min == current) {
mins[i]++;
}
}
return min;
}
private static int getMinimumCount(byte[] mins) {
int minCount = 0;
for (int i = 0; i < 6; i++) {
minCount += mins[i];
}
return minCount;
}
static boolean isDigit(char ch) {
return ch >= '0' && ch <= '9';
}
static boolean isExtendedASCII(char ch) {
return ch >= 128 && ch <= 255;
}
static boolean isNativeC40(char ch) {
return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
}
static boolean isNativeText(char ch) {
return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');
}
static boolean isNativeX12(char ch) {
return isX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
}
private static boolean isX12TermSep(char ch) {
return (ch == '\r') //CR
|| (ch == '*')
|| (ch == '>');
}
static boolean isNativeEDIFACT(char ch) {
return ch >= ' ' && ch <= '^';
}
private static boolean isSpecialB256(char ch) {
return false; //TODO NOT IMPLEMENTED YET!!!
}
/**
* Determines the number of consecutive characters that are encodable using numeric compaction.
*
* @param msg the message
* @param startpos the start position within the message
* @return the requested character count
*/
public static int determineConsecutiveDigitCount(CharSequence msg, int startpos) {
int len = msg.length();
int idx = startpos;
while (idx < len && isDigit(msg.charAt(idx))) {
idx++;
}
return idx - startpos;
}
static void illegalCharacter(char c) {
String hex = Integer.toHexString(c);
hex = "0000".substring(0, 4 - hex.length()) + hex;
throw new IllegalArgumentException("Illegal character: " + c + " (0x" + hex + ')');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,236 @@
/*
* Copyright 2006 Jeremias Maerki
*
* 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.encoder;
import com.google.zxing.Dimension;
/**
* Symbol info table for DataMatrix.
*
* @version $Id$
*/
public class SymbolInfo {
static final SymbolInfo[] PROD_SYMBOLS = {
new SymbolInfo(false, 3, 5, 8, 8, 1),
new SymbolInfo(false, 5, 7, 10, 10, 1),
/*rect*/new SymbolInfo(true, 5, 7, 16, 6, 1),
new SymbolInfo(false, 8, 10, 12, 12, 1),
/*rect*/new SymbolInfo(true, 10, 11, 14, 6, 2),
new SymbolInfo(false, 12, 12, 14, 14, 1),
/*rect*/new SymbolInfo(true, 16, 14, 24, 10, 1),
new SymbolInfo(false, 18, 14, 16, 16, 1),
new SymbolInfo(false, 22, 18, 18, 18, 1),
/*rect*/new SymbolInfo(true, 22, 18, 16, 10, 2),
new SymbolInfo(false, 30, 20, 20, 20, 1),
/*rect*/new SymbolInfo(true, 32, 24, 16, 14, 2),
new SymbolInfo(false, 36, 24, 22, 22, 1),
new SymbolInfo(false, 44, 28, 24, 24, 1),
/*rect*/new SymbolInfo(true, 49, 28, 22, 14, 2),
new SymbolInfo(false, 62, 36, 14, 14, 4),
new SymbolInfo(false, 86, 42, 16, 16, 4),
new SymbolInfo(false, 114, 48, 18, 18, 4),
new SymbolInfo(false, 144, 56, 20, 20, 4),
new SymbolInfo(false, 174, 68, 22, 22, 4),
new SymbolInfo(false, 204, 84, 24, 24, 4, 102, 42),
new SymbolInfo(false, 280, 112, 14, 14, 16, 140, 56),
new SymbolInfo(false, 368, 144, 16, 16, 16, 92, 36),
new SymbolInfo(false, 456, 192, 18, 18, 16, 114, 48),
new SymbolInfo(false, 576, 224, 20, 20, 16, 144, 56),
new SymbolInfo(false, 696, 272, 22, 22, 16, 174, 68),
new SymbolInfo(false, 816, 336, 24, 24, 16, 136, 56),
new SymbolInfo(false, 1050, 408, 18, 18, 36, 175, 68),
new SymbolInfo(false, 1304, 496, 20, 20, 36, 163, 62),
new DataMatrixSymbolInfo144(),
};
private static SymbolInfo[] symbols = PROD_SYMBOLS;
private final boolean rectangular;
private final int dataCapacity;
private final int errorCodewords;
public final int matrixWidth;
public final int matrixHeight;
private final int dataRegions;
private final int rsBlockData;
private final int rsBlockError;
/**
* Overrides the symbol info set used by this class. Used for testing purposes.
*
* @param override the symbol info set to use
*/
public static void overrideSymbolSet(SymbolInfo[] override) {
symbols = override;
}
public SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords,
int matrixWidth, int matrixHeight, int dataRegions) {
this(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions,
dataCapacity, errorCodewords);
}
SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords,
int matrixWidth, int matrixHeight, int dataRegions,
int rsBlockData, int rsBlockError) {
this.rectangular = rectangular;
this.dataCapacity = dataCapacity;
this.errorCodewords = errorCodewords;
this.matrixWidth = matrixWidth;
this.matrixHeight = matrixHeight;
this.dataRegions = dataRegions;
this.rsBlockData = rsBlockData;
this.rsBlockError = rsBlockError;
}
public static SymbolInfo lookup(int dataCodewords) {
return lookup(dataCodewords, SymbolShapeHint.FORCE_NONE, true);
}
public static SymbolInfo lookup(int dataCodewords, SymbolShapeHint shape) {
return lookup(dataCodewords, shape, true);
}
public static SymbolInfo lookup(int dataCodewords, boolean allowRectangular, boolean fail) {
SymbolShapeHint shape = allowRectangular
? SymbolShapeHint.FORCE_NONE : SymbolShapeHint.FORCE_SQUARE;
return lookup(dataCodewords, shape, fail);
}
private static SymbolInfo lookup(int dataCodewords, SymbolShapeHint shape, boolean fail) {
return lookup(dataCodewords, shape, null, null, fail);
}
public static SymbolInfo lookup(int dataCodewords,
SymbolShapeHint shape,
Dimension minSize,
Dimension maxSize,
boolean fail) {
for (SymbolInfo symbol : symbols) {
if (shape == SymbolShapeHint.FORCE_SQUARE && symbol.rectangular) {
continue;
}
if (shape == SymbolShapeHint.FORCE_RECTANGLE && !symbol.rectangular) {
continue;
}
if (minSize != null
&& (symbol.getSymbolWidth() < minSize.getWidth()
|| symbol.getSymbolHeight() < minSize.getHeight())) {
continue;
}
if (maxSize != null
&& (symbol.getSymbolWidth() > maxSize.getWidth()
|| symbol.getSymbolHeight() > maxSize.getHeight())) {
continue;
}
if (dataCodewords <= symbol.dataCapacity) {
return symbol;
}
}
if (fail) {
throw new IllegalArgumentException(
"Can't find a symbol arrangement that matches the message. Data codewords: "
+ dataCodewords);
}
return null;
}
private int getHorizontalDataRegions() {
switch (dataRegions) {
case 1:
return 1;
case 2:
case 4:
return 2;
case 16:
return 4;
case 36:
return 6;
default:
throw new IllegalStateException("Cannot handle this number of data regions");
}
}
private int getVerticalDataRegions() {
switch (dataRegions) {
case 1:
case 2:
return 1;
case 4:
return 2;
case 16:
return 4;
case 36:
return 6;
default:
throw new IllegalStateException("Cannot handle this number of data regions");
}
}
public final int getSymbolDataWidth() {
return getHorizontalDataRegions() * matrixWidth;
}
public final int getSymbolDataHeight() {
return getVerticalDataRegions() * matrixHeight;
}
public final int getSymbolWidth() {
return getSymbolDataWidth() + (getHorizontalDataRegions() * 2);
}
public final int getSymbolHeight() {
return getSymbolDataHeight() + (getVerticalDataRegions() * 2);
}
public int getCodewordCount() {
return dataCapacity + errorCodewords;
}
public int getInterleavedBlockCount() {
return dataCapacity / rsBlockData;
}
public final int getDataCapacity() {
return dataCapacity;
}
public final int getErrorCodewords() {
return errorCodewords;
}
public int getDataLengthForInterleavedBlock(int index) {
return rsBlockData;
}
public final int getErrorLengthForInterleavedBlock(int index) {
return rsBlockError;
}
@Override
public final String toString() {
return (rectangular ? "Rectangular Symbol:" : "Square Symbol:") +
" data region " + matrixWidth + 'x' + matrixHeight +
", symbol size " + getSymbolWidth() + 'x' + getSymbolHeight() +
", symbol data size " + getSymbolDataWidth() + 'x' + getSymbolDataHeight() +
", codewords " + dataCapacity + '+' + errorCodewords;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2007 Jeremias Maerki.
*
* 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.encoder;
/**
* Enumeration for DataMatrix symbol shape hint. It can be used to force square or rectangular
* symbols.
*/
public enum SymbolShapeHint {
FORCE_NONE,
FORCE_SQUARE,
FORCE_RECTANGLE,
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
final class TextEncoder extends C40Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.TEXT_ENCODATION;
}
@Override
int encodeChar(char c, StringBuilder sb) {
if (c == ' ') {
sb.append('\3');
return 1;
}
if (c >= '0' && c <= '9') {
sb.append((char) (c - 48 + 4));
return 1;
}
if (c >= 'a' && c <= 'z') {
sb.append((char) (c - 97 + 14));
return 1;
}
if (c < ' ') {
sb.append('\0'); //Shift 1 Set
sb.append(c);
return 2;
}
if (c <= '/') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 33));
return 2;
}
if (c <= '@') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 58 + 15));
return 2;
}
if (c >= '[' && c <= '_') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 91 + 22));
return 2;
}
if (c == '`') {
sb.append('\2'); //Shift 3 Set
sb.append((char) 0); // '`' - 96 == 0
return 2;
}
if (c <= 'Z') {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 65 + 1));
return 2;
}
if (c <= 127) {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 123 + 27));
return 2;
}
sb.append("\1\u001e"); //Shift 2, Upper Shift
int len = 2;
len += encodeChar((char) (c - 128), sb);
return len;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* 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.encoder;
final class X12Encoder extends C40Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.X12_ENCODATION;
}
@Override
public void encode(EncoderContext context) {
//step C
StringBuilder buffer = new StringBuilder();
while (context.hasMoreCharacters()) {
char c = context.getCurrentChar();
context.pos++;
encodeChar(c, buffer);
int count = buffer.length();
if ((count % 3) == 0) {
writeNextTriplet(context, buffer);
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
// Return to ASCII encodation, which will actually handle latch to new mode
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
break;
}
}
}
handleEOD(context, buffer);
}
@Override
int encodeChar(char c, StringBuilder sb) {
switch (c) {
case '\r':
sb.append('\0');
break;
case '*':
sb.append('\1');
break;
case '>':
sb.append('\2');
break;
case ' ':
sb.append('\3');
break;
default:
if (c >= '0' && c <= '9') {
sb.append((char) (c - 48 + 4));
} else if (c >= 'A' && c <= 'Z') {
sb.append((char) (c - 65 + 14));
} else {
HighLevelEncoder.illegalCharacter(c);
}
break;
}
return 1;
}
@Override
void handleEOD(EncoderContext context, StringBuilder buffer) {
context.updateSymbolInfo();
int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();
int count = buffer.length();
context.pos -= count;
if (context.getRemainingCharacters() > 1 || available > 1 ||
context.getRemainingCharacters() != available) {
context.writeCodeword(HighLevelEncoder.X12_UNLATCH);
}
if (context.getNewEncoding() < 0) {
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
}
}
}