mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
datamatrix copy
This commit is contained in:
160
port_src/core/DONE/datamatrix/DataMatrixReader.java
Normal file
160
port_src/core/DONE/datamatrix/DataMatrixReader.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
220
port_src/core/DONE/datamatrix/DataMatrixWriter.java
Normal file
220
port_src/core/DONE/datamatrix/DataMatrixWriter.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
443
port_src/core/DONE/datamatrix/decoder/BitMatrixParser.java
Normal file
443
port_src/core/DONE/datamatrix/decoder/BitMatrixParser.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
118
port_src/core/DONE/datamatrix/decoder/DataBlock.java
Normal file
118
port_src/core/DONE/datamatrix/decoder/DataBlock.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
125
port_src/core/DONE/datamatrix/decoder/Decoder.java
Normal file
125
port_src/core/DONE/datamatrix/decoder/Decoder.java
Normal 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];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
276
port_src/core/DONE/datamatrix/decoder/Version.java
Normal file
276
port_src/core/DONE/datamatrix/decoder/Version.java
Normal 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)))
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
383
port_src/core/DONE/datamatrix/detector/Detector.java
Normal file
383
port_src/core/DONE/datamatrix/detector/Detector.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
82
port_src/core/DONE/datamatrix/encoder/ASCIIEncoder.java
Normal file
82
port_src/core/DONE/datamatrix/encoder/ASCIIEncoder.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
75
port_src/core/DONE/datamatrix/encoder/Base256Encoder.java
Normal file
75
port_src/core/DONE/datamatrix/encoder/Base256Encoder.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
212
port_src/core/DONE/datamatrix/encoder/C40Encoder.java
Normal file
212
port_src/core/DONE/datamatrix/encoder/C40Encoder.java
Normal 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});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
198
port_src/core/DONE/datamatrix/encoder/DefaultPlacement.java
Normal file
198
port_src/core/DONE/datamatrix/encoder/DefaultPlacement.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
143
port_src/core/DONE/datamatrix/encoder/EdifactEncoder.java
Normal file
143
port_src/core/DONE/datamatrix/encoder/EdifactEncoder.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
25
port_src/core/DONE/datamatrix/encoder/Encoder.java
Normal file
25
port_src/core/DONE/datamatrix/encoder/Encoder.java
Normal 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);
|
||||
|
||||
}
|
||||
134
port_src/core/DONE/datamatrix/encoder/EncoderContext.java
Normal file
134
port_src/core/DONE/datamatrix/encoder/EncoderContext.java
Normal 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;
|
||||
}
|
||||
}
|
||||
175
port_src/core/DONE/datamatrix/encoder/ErrorCorrection.java
Normal file
175
port_src/core/DONE/datamatrix/encoder/ErrorCorrection.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
484
port_src/core/DONE/datamatrix/encoder/HighLevelEncoder.java
Normal file
484
port_src/core/DONE/datamatrix/encoder/HighLevelEncoder.java
Normal 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 + ')');
|
||||
}
|
||||
|
||||
}
|
||||
1044
port_src/core/DONE/datamatrix/encoder/MinimalEncoder.java
Executable file
1044
port_src/core/DONE/datamatrix/encoder/MinimalEncoder.java
Executable file
File diff suppressed because it is too large
Load Diff
236
port_src/core/DONE/datamatrix/encoder/SymbolInfo.java
Normal file
236
port_src/core/DONE/datamatrix/encoder/SymbolInfo.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
29
port_src/core/DONE/datamatrix/encoder/SymbolShapeHint.java
Normal file
29
port_src/core/DONE/datamatrix/encoder/SymbolShapeHint.java
Normal 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,
|
||||
|
||||
}
|
||||
81
port_src/core/DONE/datamatrix/encoder/TextEncoder.java
Normal file
81
port_src/core/DONE/datamatrix/encoder/TextEncoder.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
93
port_src/core/DONE/datamatrix/encoder/X12Encoder.java
Normal file
93
port_src/core/DONE/datamatrix/encoder/X12Encoder.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user