Issue 572 port of Data Matrix encoder from barcode4j, adapted for Android, ported by Guillaume Le Biller and stoty47 and adapted a bit more by me

git-svn-id: https://zxing.googlecode.com/svn/trunk@2569 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen@gmail.com 2013-02-07 20:17:08 +00:00
parent 2170a1cb0b
commit a642bdd839
25 changed files with 2897 additions and 0 deletions

View file

@ -37,6 +37,7 @@ evansepdx
Erik Barbara
Fred Lin (Anobiit)
gcstang
Guillaume Le Biller
Hannes Erven
hosigumayuugi
hypest (Barcorama project)
@ -89,6 +90,7 @@ Shiyuan Guo / 郭世元
ShumovichY
Simon Flannery (Ericsson)
Steven Parkes
stoty74
Suraj Supekar
Sven Klinkhamer
techinstantdx

View file

@ -0,0 +1,62 @@
/*
* Copyright 2012 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;
/**
* Simply encapsulates a width and height.
*/
public final class Dimension {
private final int width;
private final int height;
public Dimension(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException();
}
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
@Override
public boolean equals(Object other) {
if (other instanceof Dimension) {
Dimension d = (Dimension) other;
return width == d.width && height == d.height;
}
return false;
}
@Override
public int hashCode() {
return width * 32713 + height;
}
@Override
public String toString() {
return width + "x" + height;
}
}

View file

@ -35,6 +35,21 @@ public enum EncodeHintType {
*/
CHARACTER_SET,
/**
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
*/
DATA_MATRIX_SHAPE,
/**
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*/
MIN_SIZE,
/**
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*/
MAX_SIZE,
/**
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
* by format; for example it controls margin before and after the barcode horizontally for

View file

@ -17,6 +17,7 @@
package com.google.zxing;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.datamatrix.DataMatrixWriter;
import com.google.zxing.oned.CodaBarWriter;
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.oned.Code39Writer;
@ -80,6 +81,9 @@ public final class MultiFormatWriter implements Writer {
case CODABAR:
writer = new CodaBarWriter();
break;
case DATA_MATRIX:
writer = new DataMatrixWriter();
break;
default:
throw new IllegalArgumentException("No encoder available for format " + format);
}

View file

@ -0,0 +1,178 @@
/*
* 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.SymbolInfo;
import com.google.zxing.datamatrix.encoder.SymbolShapeHint;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import java.util.Map;
/**
* 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.length() == 0) {
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 are too small: " + 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;
}
Dimension requestedMinSize = (Dimension) hints.get(EncodeHintType.MIN_SIZE);
if (requestedMinSize != null) {
minSize = requestedMinSize;
}
Dimension requestedMaxSize = (Dimension) hints.get(EncodeHintType.MAX_SIZE);
if (requestedMaxSize != null) {
maxSize = requestedMaxSize;
}
}
//1. step: Data encodation
String encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize);
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);
}
/**
* 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 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);
}
/**
* Convert the ByteMatrix to BitMatrix.
*
* @param matrix The input matrix.
* @return The output matrix.
*/
private static BitMatrix convertByteMatrixToBitMatrix(ByteMatrix matrix) {
int matrixWidgth = matrix.getWidth();
int matrixHeight = matrix.getHeight();
BitMatrix output = new BitMatrix(matrixWidgth, matrixHeight);
output.clear();
for (int i = 0; i < matrixWidgth; i++) {
for (int j = 0; j < matrixHeight; j++) {
// Zero is white in the bytematrix
if (matrix.get(i, j) == 1) {
output.set(i, j);
}
}
}
return output;
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.encoder;
final class ASCIIEncoder implements Encoder {
@Override
public int getEncodingMode() {
return HighLevelEncoder.ASCII_ENCODATION;
}
@Override
public void encode(EncoderContext context) {
//step B
int n = HighLevelEncoder.determineConsecutiveDigitCount(context.msg, context.pos);
if (n >= 2) {
context.writeCodeword(encodeASCIIDigits(context.msg.charAt(context.pos),
context.msg.charAt(context.pos + 1)));
context.pos += 2;
} else {
char c = context.getCurrentChar();
int newMode = HighLevelEncoder.lookAheadTest(context.msg, context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
switch (newMode) {
case HighLevelEncoder.BASE256_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_BASE256);
context.signalEncoderChange(HighLevelEncoder.BASE256_ENCODATION);
return;
case HighLevelEncoder.C40_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);
context.signalEncoderChange(HighLevelEncoder.C40_ENCODATION);
return;
case HighLevelEncoder.X12_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_ANSIX12);
context.signalEncoderChange(HighLevelEncoder.X12_ENCODATION);
break;
case HighLevelEncoder.TEXT_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_TEXT);
context.signalEncoderChange(HighLevelEncoder.TEXT_ENCODATION);
break;
case HighLevelEncoder.EDIFACT_ENCODATION:
context.writeCodeword(HighLevelEncoder.LATCH_TO_EDIFACT);
context.signalEncoderChange(HighLevelEncoder.EDIFACT_ENCODATION);
break;
default:
throw new IllegalStateException("Illegal mode: " + newMode);
}
} else if (HighLevelEncoder.isExtendedASCII(c)) {
context.writeCodeword(HighLevelEncoder.UPPER_SHIFT);
context.writeCodeword((char) (c - 128 + 1));
context.pos++;
} else {
context.writeCodeword((char) (c + 1));
context.pos++;
}
}
}
private static char encodeASCIIDigits(char digit1, char digit2) {
if (HighLevelEncoder.isDigit(digit1) && HighLevelEncoder.isDigit(digit2)) {
int num = (digit1 - 48) * 10 + (digit2 - 48);
return (char) (num + 130);
}
throw new IllegalArgumentException("not digits: " + digit1 + digit2);
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.msg, context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
context.signalEncoderChange(newMode);
break;
}
}
int dataCount = buffer.length() - 1;
int lengthFieldSize = 1;
int currentSize = context.getCodewordCount() + dataCount + lengthFieldSize;
context.updateSymbolInfo(currentSize);
boolean mustPad = (context.symbolInfo.dataCapacity - currentSize) > 0;
if (context.hasMoreCharacters() || mustPad) {
if (dataCount <= 249) {
buffer.setCharAt(0, (char) dataCount);
} else if (dataCount > 249 && dataCount <= 1555) {
buffer.setCharAt(0, (char) ((dataCount / 250) + 249));
buffer.insert(1, (char) (dataCount % 250));
} else {
throw new IllegalStateException(
"Message length not in valid ranges: " + dataCount);
}
}
for (int i = 0, c = buffer.length(); i < c; i++) {
context.writeCodeword(randomize255State(
buffer.charAt(i), context.getCodewordCount() + 1));
}
}
private static char randomize255State(char ch, int codewordPosition) {
int pseudoRandom = ((149 * codewordPosition) % 255) + 1;
int tempVariable = ch + pseudoRandom;
if (tempVariable <= 255) {
return (char) tempVariable;
} else {
return (char) (tempVariable - 256);
}
}
}

View file

@ -0,0 +1,181 @@
/*
* 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;
}
@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.symbolInfo.dataCapacity - curCodewordCount;
if (!context.hasMoreCharacters()) {
//Avoid having a single C40 value in the last triplet
StringBuilder removed = new StringBuilder();
if ((buffer.length() % 3) == 2) {
if (available < 2 || available > 2) {
lastCharSize = backtrackOneCharacter(context, buffer, removed,
lastCharSize);
}
}
while ((buffer.length() % 3) == 1
&& ((lastCharSize <= 3 && available != 1) || lastCharSize > 3)) {
lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
}
break;
}
int count = buffer.length();
if ((count % 3) == 0) {
int newMode = HighLevelEncoder.lookAheadTest(context.msg, context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
context.signalEncoderChange(newMode);
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, 0));
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.symbolInfo.dataCapacity - 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;
} else if (c >= '0' && c <= '9') {
sb.append((char) (c - 48 + 4));
return 1;
} else if (c >= 'A' && c <= 'Z') {
sb.append((char) (c - 65 + 14));
return 1;
} else if (c >= '\0' && c <= '\u001f') {
sb.append('\0'); //Shift 1 Set
sb.append(c);
return 2;
} else if (c >= '!' && c <= '/') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 33));
return 2;
} else if (c >= ':' && c <= '@') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 58 + 15));
return 2;
} else if (c >= '[' && c <= '_') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 91 + 22));
return 2;
} else if (c >= '\u0060' && c <= '\u007f') {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 96));
return 2;
} else if (c >= '\u0080') {
sb.append("\1\u001e"); //Shift 2, Upper Shift
int len = 2;
len += encodeChar((char) (c - 128), sb);
return len;
} else {
throw new IllegalArgumentException("Illegal character: " + c);
}
}
private static String encodeToCodewords(CharSequence sb, int startPos) {
char c1 = sb.charAt(startPos);
char c2 = sb.charAt(startPos + 1);
char c3 = sb.charAt(startPos + 2);
int v = (1600 * c1) + (40 * c2) + c3 + 1;
char cw1 = (char) (v / 256);
char cw2 = (char) (v % 256);
return new String(new char[] {cw1, cw2});
}
}

View file

@ -0,0 +1,37 @@
/*
* 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);
this.rsBlockData = -1; //special! see below
this.rsBlockError = 62;
}
@Override
public int getInterleavedBlockCount() {
return 10;
}
@Override
public int getDataLengthForInterleavedBlock(int index) {
return (index <= 8) ? 156 : 155;
}
}

View file

@ -0,0 +1,198 @@
/*
* Copyright 2006 Jeremias Maerki.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.encoder;
import java.util.Arrays;
/**
* Symbol Character Placement Program. Adapted from Annex M.1 in ISO/IEC 16022:2000(E).
*/
public class DefaultPlacement {
private final String 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(String 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;
}
final void setBit(int col, int row, boolean bit) {
bits[row * numcols + col] = bit ? (byte) 1 : (byte) 0;
}
final boolean hasBit(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) && !hasBit(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) && !hasBit(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 righthand corner is untouched, fill in fixed pattern */
if (!hasBit(numcols - 1, numrows - 1)) {
setBit(numcols - 1, numrows - 1, true);
setBit(numcols - 2, numrows - 2, true);
}
}
private void module(int row, int col, int pos, int bit) {
if (row < 0) {
row += numrows;
col += 4 - ((numrows + 4) % 8);
}
if (col < 0) {
col += numcols;
row += 4 - ((numcols + 4) % 8);
}
// Note the conversion:
int v = codewords.charAt(pos);
v &= 1 << (8 - bit);
setBit(col, row, v != 0);
}
/**
* Places the 8 bits of a utah-shaped symbol character in ECC200.
*
* @param row the row
* @param col the column
* @param pos character position
*/
private void utah(int row, int col, int pos) {
module(row - 2, col - 2, pos, 1);
module(row - 2, col - 1, pos, 2);
module(row - 1, col - 2, pos, 3);
module(row - 1, col - 1, pos, 4);
module(row - 1, col, pos, 5);
module(row, col - 2, pos, 6);
module(row, col - 1, pos, 7);
module(row, col, pos, 8);
}
private void corner1(int pos) {
module(numrows - 1, 0, pos, 1);
module(numrows - 1, 1, pos, 2);
module(numrows - 1, 2, pos, 3);
module(0, numcols - 2, pos, 4);
module(0, numcols - 1, pos, 5);
module(1, numcols - 1, pos, 6);
module(2, numcols - 1, pos, 7);
module(3, numcols - 1, pos, 8);
}
private void corner2(int pos) {
module(numrows - 3, 0, pos, 1);
module(numrows - 2, 0, pos, 2);
module(numrows - 1, 0, pos, 3);
module(0, numcols - 4, pos, 4);
module(0, numcols - 3, pos, 5);
module(0, numcols - 2, pos, 6);
module(0, numcols - 1, pos, 7);
module(1, numcols - 1, pos, 8);
}
private void corner3(int pos) {
module(numrows - 3, 0, pos, 1);
module(numrows - 2, 0, pos, 2);
module(numrows - 1, 0, pos, 3);
module(0, numcols - 2, pos, 4);
module(0, numcols - 1, pos, 5);
module(1, numcols - 1, pos, 6);
module(2, numcols - 1, pos, 7);
module(3, numcols - 1, pos, 8);
}
private void corner4(int pos) {
module(numrows - 1, 0, pos, 1);
module(numrows - 1, numcols - 1, pos, 2);
module(0, numcols - 3, pos, 3);
module(0, numcols - 2, pos, 4);
module(0, numcols - 1, pos, 5);
module(1, numcols - 3, pos, 6);
module(1, numcols - 2, pos, 7);
module(1, numcols - 1, pos, 8);
}
}

View file

@ -0,0 +1,137 @@
/*
* 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, 0));
buffer.delete(0, 4);
int newMode = HighLevelEncoder.lookAheadTest(context.msg, context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
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.symbolInfo.dataCapacity - context.getCodewordCount();
int remaining = context.getRemainingCharacters();
if (remaining == 0 && available <= 2) {
return; //No unlatch
}
}
if (count > 4) {
throw new IllegalStateException("Count must not exceed 4");
}
int restChars = count - 1;
String encoded = encodeToCodewords(buffer, 0);
boolean endOfSymbolReached = !context.hasMoreCharacters();
boolean restInAscii = endOfSymbolReached && restChars <= 2;
if (restChars <= 2) {
context.updateSymbolInfo(context.getCodewordCount() + restChars);
int available = context.symbolInfo.dataCapacity - 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 startPos) {
int len = sb.length() - startPos;
if (len == 0) {
throw new IllegalStateException("StringBuilder must not be empty");
}
char c1 = sb.charAt(startPos);
char c2 = len >= 2 ? sb.charAt(startPos + 1) : 0;
char c3 = len >= 3 ? sb.charAt(startPos + 2) : 0;
char c4 = len >= 4 ? sb.charAt(startPos + 3) : 0;
int v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;
char cw1 = (char) ((v >> 16) & 255);
char cw2 = (char) ((v >> 8) & 255);
char cw3 = (char) (v & 255);
StringBuilder res = new StringBuilder(3);
res.append(cw1);
if (len >= 2) {
res.append(cw2);
}
if (len >= 3) {
res.append(cw3);
}
return res.toString();
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright 2006-2007 Jeremias Maerki.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.encoder;
interface Encoder {
int getEncodingMode();
void encode(EncoderContext context);
}

View file

@ -0,0 +1,122 @@
/*
* 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.Charset;
final class EncoderContext {
String msg;
private SymbolShapeHint shape;
private Dimension minSize;
private Dimension maxSize;
StringBuilder codewords;
int pos;
int newEncoding;
SymbolInfo symbolInfo;
private int skipAtEnd;
EncoderContext(String msg) {
//From this point on Strings are not Unicode anymore!
byte[] msgBinary = msg.getBytes(Charset.forName("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 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 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 void updateSymbolInfo() {
updateSymbolInfo(getCodewordCount());
}
public void updateSymbolInfo(int len) {
if (this.symbolInfo == null || len > this.symbolInfo.dataCapacity) {
this.symbolInfo = SymbolInfo.lookup(len, shape, minSize, maxSize, true);
}
}
public void resetSymbolInfo() {
this.symbolInfo = null;
}
}

View file

@ -0,0 +1,184 @@
/*
* 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 <<= 1;
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.dataCapacity) {
throw new IllegalArgumentException(
"The number of codewords does not match the selected symbol");
}
StringBuilder sb = new StringBuilder(symbolInfo.dataCapacity + symbolInfo.errorCodewords);
sb.append(codewords);
int blockCount = symbolInfo.getInterleavedBlockCount();
if (blockCount == 1) {
String ecc = createECCBlock(codewords, symbolInfo.errorCodewords);
sb.append(ecc);
} else {
sb.setLength(sb.capacity());
int[] dataSizes = new int[blockCount];
int[] errorSizes = new int[blockCount];
int[] startPos = new int[blockCount];
for (int i = 0; i < blockCount; i++) {
dataSizes[i] = symbolInfo.getDataLengthForInterleavedBlock(i + 1);
errorSizes[i] = symbolInfo.getErrorLengthForInterleavedBlock(i + 1);
startPos[i] = 0;
if (i > 0) {
startPos[i] = startPos[i - 1] + dataSizes[i];
}
}
for (int block = 0; block < blockCount; block++) {
StringBuilder temp = new StringBuilder(dataSizes[block]);
for (int d = block; d < symbolInfo.dataCapacity; 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.dataCapacity + e, ecc.charAt(pos++));
}
}
}
return sb.toString();
}
private static String createECCBlock(CharSequence codewords, int numECWords) {
return createECCBlock(codewords, 0, codewords.length(), numECWords);
}
private static String createECCBlock(CharSequence codewords, int start, int len, 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 = start; i < start + len; i++) {
int m = ecc[numECWords - 1] ^ codewords.charAt(i);
for (int k = numECWords - 1; k > 0; k--) {
if (m != 0 && poly[k] != 0) {
ecc[k] = (char) (ecc[k - 1] ^ ALOG[(LOG[m] + LOG[poly[k]]) % 255]);
} else {
ecc[k] = ecc[k - 1];
}
}
if (m != 0 && poly[0] != 0) {
ecc[0] = (char) ALOG[(LOG[m] + LOG[poly[0]]) % 255];
} else {
ecc[0] = 0;
}
}
char[] eccReversed = new char[numECWords];
for (int i = 0; i < numECWords; i++) {
eccReversed[i] = ecc[numECWords - i - 1];
}
return String.valueOf(eccReversed);
}
}

View file

@ -0,0 +1,446 @@
/*
* 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.Charset;
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
*/
private static final String MACRO_05_HEADER = "[)>\u001E05\u001D";
/**
* 06 Macro header
*/
private static final String MACRO_06_HEADER = "[)>\u001E06\u001D";
/**
* Macro trailer
*/
private 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() {
}
/**
* Converts the message to a byte array using the default encoding (cp437) as defined by the
* specification
*
* @param msg the message
* @return the byte array of the message
*/
public static byte[] getBytesForMessage(String msg) {
return msg.getBytes(Charset.forName("cp437")); //See 4.4.3 and annex B of ISO/IEC 15438:2001(E)
}
private static char randomize253State(char ch, int codewordPosition) {
int pseudoRandom = ((149 * codewordPosition) % 253) + 1;
int tempVariable = ch + pseudoRandom;
return tempVariable <= 254 ? (char) tempVariable : (char) (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);
}
/**
* 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) {
//the codewords 0..255 are encoded as Unicode characters
Encoder[] encoders = {
new ASCIIEncoder(), new 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
while (context.hasMoreCharacters()) {
encoders[encodingMode].encode(context);
if (context.newEncoding >= 0) {
encodingMode = context.newEncoding;
context.resetEncoderSignal();
}
}
int len = context.codewords.length();
context.updateSymbolInfo();
int capacity = context.symbolInfo.dataCapacity;
if (len < capacity) {
if (encodingMode != ASCII_ENCODATION && encodingMode != BASE256_ENCODATION) {
context.writeCodeword('\u00fe'); //Unlatch (254)
}
}
//Padding
StringBuilder codewords = context.codewords;
if (codewords.length() < capacity) {
codewords.append(PAD);
}
while (codewords.length() < capacity) {
codewords.append(randomize253State(PAD, codewords.length() + 1));
}
return context.codewords.toString();
}
static int lookAheadTest(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;
while (true) {
//step K
if ((startpos + charsProcessed) == msg.length()) {
int min = Integer.MAX_VALUE;
byte[] mins = new byte[6];
int[] intCharCounts = new int[6];
min = findMinimums(charCounts, intCharCounts, min, mins);
int minCount = getMinimumCount(mins);
if (intCharCounts[ASCII_ENCODATION] == min) {
return ASCII_ENCODATION;
}
if (minCount == 1 && mins[BASE256_ENCODATION] > 0) {
return BASE256_ENCODATION;
}
if (minCount == 1 && mins[EDIFACT_ENCODATION] > 0) {
return EDIFACT_ENCODATION;
}
if (minCount == 1 && mins[TEXT_ENCODATION] > 0) {
return TEXT_ENCODATION;
}
if (minCount == 1 && 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.5;
} else if (isExtendedASCII(c)) {
charCounts[ASCII_ENCODATION] = (int) Math.ceil(charCounts[ASCII_ENCODATION]);
charCounts[ASCII_ENCODATION] += 2;
} else {
charCounts[ASCII_ENCODATION] = (int) 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;
} else {
charCounts[BASE256_ENCODATION]++;
}
//step R
if (charsProcessed >= 4) {
int[] intCharCounts = new int[6];
byte[] mins = new byte[6];
findMinimums(charCounts, intCharCounts, Integer.MAX_VALUE, mins);
int minCount = getMinimumCount(mins);
if (intCharCounts[ASCII_ENCODATION] < intCharCounts[BASE256_ENCODATION]
&& intCharCounts[ASCII_ENCODATION] < intCharCounts[C40_ENCODATION]
&& intCharCounts[ASCII_ENCODATION] < intCharCounts[TEXT_ENCODATION]
&& intCharCounts[ASCII_ENCODATION] < intCharCounts[X12_ENCODATION]
&& intCharCounts[ASCII_ENCODATION] < intCharCounts[EDIFACT_ENCODATION]) {
return ASCII_ENCODATION;
}
if (intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION]
|| (mins[C40_ENCODATION] + mins[TEXT_ENCODATION] + mins[X12_ENCODATION] + mins[EDIFACT_ENCODATION]) == 0) {
return BASE256_ENCODATION;
}
if (minCount == 1 && mins[EDIFACT_ENCODATION] > 0) {
return EDIFACT_ENCODATION;
}
if (minCount == 1 && mins[TEXT_ENCODATION] > 0) {
return TEXT_ENCODATION;
}
if (minCount == 1 && mins[X12_ENCODATION] > 0) {
return X12_ENCODATION;
}
if (intCharCounts[C40_ENCODATION] + 1 < intCharCounts[ASCII_ENCODATION]
&& intCharCounts[C40_ENCODATION] + 1 < intCharCounts[BASE256_ENCODATION]
&& intCharCounts[C40_ENCODATION] + 1 < intCharCounts[EDIFACT_ENCODATION]
&& intCharCounts[C40_ENCODATION] + 1 < 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 findMinimums(float[] charCounts, int[] intCharCounts, int min, byte[] mins) {
Arrays.fill(mins, (byte) 0);
for (int i = 0; i < 6; i++) {
intCharCounts[i] = (int) Math.ceil(charCounts[i]);
int current = intCharCounts[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;
}
private static boolean isNativeC40(char ch) {
return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
}
private static boolean isNativeText(char ch) {
return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');
}
private 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 == '>');
}
private 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 count = 0;
int len = msg.length();
int idx = startpos;
if (idx < len) {
char ch = msg.charAt(idx);
while (isDigit(ch) && idx < len) {
count++;
idx++;
if (idx < len) {
ch = msg.charAt(idx);
}
}
}
return count;
}
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 + ')');
}
}

View file

@ -0,0 +1,232 @@
/*
* 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 {
public 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;
/**
* 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;
}
private final boolean rectangular;
final int dataCapacity;
final int errorCodewords;
public final int matrixWidth;
public final int matrixHeight;
private final int dataRegions;
int rsBlockData;
int rsBlockError;
public SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords,
int matrixWidth, int matrixHeight, int dataRegions) {
this(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions,
dataCapacity, errorCodewords);
}
private 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;
}
final int getHorizontalDataRegions() {
switch (dataRegions) {
case 1:
return 1;
case 2:
return 2;
case 4:
return 2;
case 16:
return 4;
case 36:
return 6;
default:
throw new IllegalStateException("Cannot handle this number of data regions");
}
}
final int getVerticalDataRegions() {
switch (dataRegions) {
case 1:
return 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 int getDataLengthForInterleavedBlock(int index) {
return rsBlockData;
}
public final int getErrorLengthForInterleavedBlock(int index) {
return rsBlockError;
}
@Override
public final String toString() {
StringBuilder sb = new StringBuilder();
sb.append(rectangular ? "Rectangular Symbol:" : "Square Symbol:");
sb.append(" data region ").append(matrixWidth).append('x').append(matrixHeight);
sb.append(", symbol size ").append(getSymbolWidth()).append('x').append(getSymbolHeight());
sb.append(", symbol data size ").append(getSymbolDataWidth()).append('x').append(getSymbolDataHeight());
sb.append(", codewords ").append(dataCapacity).append('+').append(errorCodewords);
return sb.toString();
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2007 Jeremias Maerki.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.datamatrix.encoder;
/**
* Enumeration for DataMatrix symbol shape hint. It can be used to force square or rectangular
* symbols.
*/
public enum SymbolShapeHint {
FORCE_NONE,
FORCE_SQUARE,
FORCE_RECTANGLE,
}

View file

@ -0,0 +1,85 @@
/*
* 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 >= '\0' && c <= '\u001f') {
sb.append('\0'); //Shift 1 Set
sb.append(c);
return 2;
}
if (c >= '!' && c <= '/') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 33));
return 2;
}
if (c >= ':' && 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 == '\u0060') {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 96));
return 2;
}
if (c >= 'A' && c <= 'Z') {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 65 + 1));
return 2;
}
if (c >= '{' && c <= '\u007f') {
sb.append('\2'); //Shift 3 Set
sb.append((char) (c - 123 + 27));
return 2;
}
if (c >= '\u0080') {
sb.append("\1\u001e"); //Shift 2, Upper Shift
int len = 2;
len += encodeChar((char) (c - 128), sb);
return len;
}
HighLevelEncoder.illegalCharacter(c);
return -1;
}
}

View file

@ -0,0 +1,88 @@
/*
* 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.msg, context.pos, getEncodingMode());
if (newMode != getEncodingMode()) {
context.signalEncoderChange(newMode);
break;
}
}
}
handleEOD(context, buffer);
}
@Override
int encodeChar(char c, StringBuilder sb) {
if (c == '\r') {
sb.append('\0');
} else if (c == '*') {
sb.append('\1');
} else if (c == '>') {
sb.append('\2');
} else if (c == ' ') {
sb.append('\3');
} else 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);
}
return 1;
}
@Override
void handleEOD(EncoderContext context, StringBuilder buffer) {
context.updateSymbolInfo();
int available = context.symbolInfo.dataCapacity - context.getCodewordCount();
int count = buffer.length();
if (count == 2) {
context.writeCodeword(HighLevelEncoder.X12_UNLATCH);
context.pos -= 2;
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
} else if (count == 1) {
context.pos--;
if (available > 1) {
context.writeCodeword(HighLevelEncoder.X12_UNLATCH);
}
//NOP - No unlatch necessary
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
}
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.common.BitMatrix;
import com.google.zxing.datamatrix.encoder.SymbolShapeHint;
import org.junit.Assert;
import org.junit.Test;
import java.util.EnumMap;
import java.util.Map;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported and expanded from C++
*/
public final class DataMatrixWriterTestCase extends Assert {
@Test
public void testDataMatrixImageWriter() {
DataMatrixWriter writer = new DataMatrixWriter();
Map<EncodeHintType,Object> hints = new EnumMap<EncodeHintType,Object>(EncodeHintType.class);
hints.put(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
int bigEnough = 64;
BitMatrix matrix = writer.encode("Hello Google", BarcodeFormat.DATA_MATRIX, bigEnough, bigEnough, hints);
assertNotNull(matrix);
assertTrue(bigEnough >= matrix.getWidth());
assertTrue(bigEnough >= matrix.getHeight());
}
@Test
public void testDataMatrixWriter() {
DataMatrixWriter writer = new DataMatrixWriter();
Map<EncodeHintType,Object> hints = new EnumMap<EncodeHintType,Object>(EncodeHintType.class);
hints.put(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
int bigEnough = 14;
BitMatrix matrix = writer.encode("Hello Me", BarcodeFormat.DATA_MATRIX, bigEnough, bigEnough, hints);
assertNotNull(matrix);
assertEquals(bigEnough, matrix.getWidth());
assertEquals(bigEnough, matrix.getHeight());
}
@Test
public void testDataMatrixTooSmall() {
// The DataMatrix will not fit in this size, so the matrix should come back bigger
int tooSmall = 8;
DataMatrixWriter writer = new DataMatrixWriter();
BitMatrix matrix = writer.encode("http://www.google.com/", BarcodeFormat.DATA_MATRIX, tooSmall, tooSmall, null);
assertNotNull(matrix);
assertTrue(tooSmall < matrix.getWidth());
assertTrue(tooSmall < matrix.getHeight());
}
}

View file

@ -0,0 +1,51 @@
/*
* 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 DebugPlacement extends DefaultPlacement {
DebugPlacement(String codewords, int numcols, int numrows) {
super(codewords, numcols, numrows);
}
public String toBitFieldString() {
byte[] bits = getBits();
StringBuilder sb = new StringBuilder(bits.length);
for (byte bit : bits) {
sb.append(bit == 1 ? '1' : '0');
}
return sb.toString();
}
String[] toBitFieldStringArray() {
byte[] bits = getBits();
int numrows = getNumrows();
int numcols = getNumcols();
String[] array = new String[numrows];
int startpos = 0;
for (int row = 0; row < numrows; row++) {
StringBuilder sb = new StringBuilder(bits.length);
for (int i = 0; i < numcols; i++) {
sb.append(bits[startpos + i] == 1 ? '1' : '0');
}
array[row] = sb.toString();
startpos += numcols;
}
return array;
}
}

View file

@ -0,0 +1,41 @@
/*
* 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 org.junit.Assert;
import org.junit.Test;
/**
* Tests for the ECC200 error correction.
*/
public final class ErrorCorrectionTestCase extends Assert {
@Test
public void testRS() {
//Sample from Annexe R in ISO/IEC 16022:2000(E)
char[] cw = {142, 164, 186};
SymbolInfo symbolInfo = SymbolInfo.lookup(3);
String s = ErrorCorrection.encodeECC200(String.valueOf(cw), symbolInfo);
assertEquals("142 164 186 114 25 5 88 102", HighLevelEncodeTestCase.visualize(s));
//"A" encoded (ASCII encoding + 2 padding characters)
cw = new char[]{66, 129, 70};
s = ErrorCorrection.encodeECC200(String.valueOf(cw), symbolInfo);
assertEquals("66 129 70 138 234 82 82 95", HighLevelEncodeTestCase.visualize(s));
}
}

View file

@ -0,0 +1,379 @@
/*
* 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 junit.framework.ComparisonFailure;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests for {@link HighLevelEncoder}.
*/
public final class HighLevelEncodeTestCase extends Assert {
private static final SymbolInfo[] TEST_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, 13, 0, 0, 0, 1),
new SymbolInfo(false, 77, 0, 0, 0, 1)
//The last entries are fake entries to test special conditions with C40 encoding
};
private static void useTestSymbols() {
SymbolInfo.overrideSymbolSet(TEST_SYMBOLS);
}
private static void resetSymbols() {
SymbolInfo.overrideSymbolSet(SymbolInfo.PROD_SYMBOLS);
}
@Test
public void testASCIIEncodation() {
String visualized = encodeHighLevel("123456");
assertEquals("142 164 186", visualized);
visualized = encodeHighLevel("123456£");
assertEquals("142 164 186 235 36", visualized);
visualized = encodeHighLevel("30Q324343430794<OQQ");
assertEquals("160 82 162 173 173 173 137 224 61 80 82 82", visualized);
}
@Test
public void testC40EncodationBasic1() {
String visualized = encodeHighLevel("AIMAIMAIM");
assertEquals("230 91 11 91 11 91 11 254", visualized);
//230 shifts to C40 encodation, 254 unlatches, "else" case
}
@Test
public void testC40EncodationBasic2() {
String visualized = encodeHighLevel("AIMAIAB");
assertEquals("230 91 11 90 255 254 67 129", visualized);
//"B" is normally encoded as "15" (one C40 value)
//"else" case: "B" is encoded as ASCII
visualized = encodeHighLevel("AIMAIAb");
assertEquals("66 74 78 66 74 66 99 129", visualized); //Encoded as ASCII
//Alternative solution:
//assertEquals("230 91 11 90 255 254 99 129", visualized);
//"b" is normally encoded as "Shift 3, 2" (two C40 values)
//"else" case: "b" is encoded as ASCII
visualized = encodeHighLevel("AIMAIMAIMË");
assertEquals("230 91 11 91 11 91 11 254 235 76", visualized);
//Alternative solution:
//assertEquals("230 91 11 91 11 91 11 11 9 254", visualized);
//Expl: 230 = shift to C40, "91 11" = "AIM",
//"11 9" = "<EFBFBD>" = "Shift 2, UpperShift, <char>
//"else" case
visualized = encodeHighLevel("AIMAIMAIMë");
assertEquals("230 91 11 91 11 91 11 254 235 108", visualized); //Activate when additional rectangulars are available
//Expl: 230 = shift to C40, "91 11" = "AIM",
//"<EFBFBD>" in C40 encodes to: 1 30 2 11 which doesn't fit into a triplet
//"10 243" =
//254 = unlatch, 235 = Upper Shift, 108 = <EFBFBD> = 0xEB/235 - 128 + 1
//"else" case
}
@Test
public void testC40EncodationSpecExample() {
//Example in Figure 1 in the spec
String visualized = encodeHighLevel("A1B2C3D4E5F6G7H8I9J0K1L2");
assertEquals("230 88 88 40 8 107 147 59 67 126 206 78 126 144 121 35 47 254", visualized);
}
@Test
public void testC40EncodationSpecialCases1() {
//Special tests avoiding ultra-long test strings because these tests are only used
//with the 16x48 symbol (47 data codewords)
useTestSymbols();
String visualized = encodeHighLevel("AIMAIMAIMAIMAIMAIM");
assertEquals("230 91 11 91 11 91 11 91 11 91 11 91 11", visualized);
//case "a": Unlatch is not required
visualized = encodeHighLevel("AIMAIMAIMAIMAIMAI");
assertEquals("230 91 11 91 11 91 11 91 11 91 11 90 241", visualized);
//case "b": Add trailing shift 0 and Unlatch is not required
visualized = encodeHighLevel("AIMAIMAIMAIMAIMA");
assertEquals("230 91 11 91 11 91 11 91 11 91 11 254 66", visualized);
//case "c": Unlatch and write last character in ASCII
resetSymbols();
visualized = encodeHighLevel("AIMAIMAIMAIMAIMAI");
assertEquals("230 91 11 91 11 91 11 91 11 91 11 254 66 74 129 237", visualized);
visualized = encodeHighLevel("AIMAIMAIMA");
assertEquals("230 91 11 91 11 91 11 66", visualized);
//case "d": Skip Unlatch and write last character in ASCII
}
@Test
public void testC40EncodationSpecialCases2() {
String visualized = encodeHighLevel("AIMAIMAIMAIMAIMAIMAI");
assertEquals("230 91 11 91 11 91 11 91 11 91 11 91 11 254 66 74", visualized);
//available > 2, rest = 2 --> unlatch and encode as ASCII
}
@Test
public void testTextEncodation() {
String visualized = encodeHighLevel("aimaimaim");
assertEquals("239 91 11 91 11 91 11 254", visualized);
//239 shifts to Text encodation, 254 unlatches
visualized = encodeHighLevel("aimaimaim'");
assertEquals("239 91 11 91 11 91 11 254 40 129", visualized);
//assertEquals("239 91 11 91 11 91 11 7 49 254", visualized);
//This is an alternative, but doesn't strictly follow the rules in the spec.
visualized = encodeHighLevel("aimaimaIm");
assertEquals("239 91 11 91 11 87 218 110", visualized);
visualized = encodeHighLevel("aimaimaimB");
assertEquals("239 91 11 91 11 91 11 254 67 129", visualized);
visualized = encodeHighLevel("aimaimaim{txt}\u0004");
assertEquals("239 91 11 91 11 91 11 16 218 236 107 181 69 254 129 237", visualized);
}
@Test
public void testX12Encodation() {
//238 shifts to X12 encodation, 254 unlatches
String visualized = encodeHighLevel("ABC>ABC123>AB");
assertEquals("238 89 233 14 192 100 207 44 31 67", visualized);
visualized = encodeHighLevel("ABC>ABC123>ABC");
assertEquals("238 89 233 14 192 100 207 44 31 254 67 68", visualized);
visualized = encodeHighLevel("ABC>ABC123>ABCD");
assertEquals("238 89 233 14 192 100 207 44 31 96 82 254", visualized);
visualized = encodeHighLevel("ABC>ABC123>ABCDE");
assertEquals("238 89 233 14 192 100 207 44 31 96 82 70", visualized);
visualized = encodeHighLevel("ABC>ABC123>ABCDEF");
assertEquals("238 89 233 14 192 100 207 44 31 96 82 254 70 71 129 237", visualized);
}
@Test
public void testEDIFACTEncodation() {
//240 shifts to EDIFACT encodation
String visualized = encodeHighLevel(".A.C1.3.DATA.123DATA.123DATA");
assertEquals("240 184 27 131 198 236 238 16 21 1 187 28 179 16 21 1 187 28 179 16 21 1",
visualized);
visualized = encodeHighLevel(".A.C1.3.X.X2..");
assertEquals("240 184 27 131 198 236 238 98 230 50 47 47", visualized);
visualized = encodeHighLevel(".A.C1.3.X.X2.");
assertEquals("240 184 27 131 198 236 238 98 230 50 47 129", visualized);
visualized = encodeHighLevel(".A.C1.3.X.X2");
assertEquals("240 184 27 131 198 236 238 98 230 50", visualized);
visualized = encodeHighLevel(".A.C1.3.X.X");
assertEquals("240 184 27 131 198 236 238 98 230 31", visualized);
visualized = encodeHighLevel(".A.C1.3.X.");
assertEquals("240 184 27 131 198 236 238 98 231 192", visualized);
visualized = encodeHighLevel(".A.C1.3.X");
assertEquals("240 184 27 131 198 236 238 89", visualized);
//Checking temporary unlatch from EDIFACT
visualized = encodeHighLevel(".XXX.XXX.XXX.XXX.XXX.XXX.üXX.XXX.XXX.XXX.XXX.XXX.XXX");
assertEquals("240 185 134 24 185 134 24 185 134 24 185 134 24 185 134 24 185 134 24"
+ " 124 47 235 125 240" //<-- this is the temporary unlatch
+ " 97 139 152 97 139 152 97 139 152 97 139 152 97 139 152 97 139 152 89 89",
visualized);
}
@Test
public void testBase256Encodation() {
//231 shifts to Base256 encodation
String visualized = encodeHighLevel("«äöüé»");
assertEquals("231 44 108 59 226 126 1 104", visualized);
visualized = encodeHighLevel("«äöüéà»");
assertEquals("231 51 108 59 226 126 1 141 254 129", visualized);
visualized = encodeHighLevel("«äöüéàá»");
assertEquals("231 44 108 59 226 126 1 141 36 147", visualized);
visualized = encodeHighLevel(" 23£"); //ASCII only (for reference)
assertEquals("33 153 235 36 129", visualized);
visualized = encodeHighLevel("«äöüé» 234"); //Mixed Base256 + ASCII
assertEquals("231 51 108 59 226 126 1 104 99 153 53 129", visualized);
visualized = encodeHighLevel("«äöüé» 23£ 1234567890123456789");
assertEquals("231 55 108 59 226 126 1 104 99 10 161 167 185 142 164 186 208"
+ " 220 142 164 186 208 58 129 59 209 104 254 150 45", visualized);
visualized = encodeHighLevel(createBinaryMessage(20));
assertEquals("231 44 108 59 226 126 1 141 36 5 37 187 80 230 123 17 166 60 210 103 253 150",
visualized);
visualized = encodeHighLevel(createBinaryMessage(19)); //padding necessary at the end
assertEquals("231 63 108 59 226 126 1 141 36 5 37 187 80 230 123 17 166 60 210 103 1 129",
visualized);
visualized = encodeHighLevel(createBinaryMessage(276));
assertStartsWith("231 38 219 2 208 120 20 150 35", visualized);
assertEndsWith("146 40 194 129", visualized);
visualized = encodeHighLevel(createBinaryMessage(277));
assertStartsWith("231 38 220 2 208 120 20 150 35", visualized);
assertEndsWith("146 40 190 87", visualized);
}
private static String createBinaryMessage(int len) {
StringBuilder sb = new StringBuilder();
sb.append("«äöüéàá-");
for (int i = 0; i < len - 9; i++) {
sb.append('\u00B7');
}
sb.append('»');
return sb.toString();
}
private static void assertStartsWith(String expected, String actual) {
if (!actual.startsWith(expected)) {
throw new ComparisonFailure(null, expected, actual.substring(0, expected.length()));
}
}
private static void assertEndsWith(String expected, String actual) {
if (!actual.endsWith(expected)) {
throw new ComparisonFailure(null, expected, actual.substring(actual.length() - expected.length()));
}
}
@Test
public void testUnlatchingFromC40() {
String visualized = encodeHighLevel("AIMAIMAIMAIMaimaimaim");
assertEquals("230 91 11 91 11 91 11 254 66 74 78 239 91 11 91 11 91 11", visualized);
}
@Test
public void testUnlatchingFromText() {
String visualized = encodeHighLevel("aimaimaimaim12345678");
assertEquals("239 91 11 91 11 91 11 91 11 254 142 164 186 208 129 237", visualized);
}
@Test
public void testHelloWorld() {
String visualized = encodeHighLevel("Hello World!");
assertEquals("73 239 116 130 175 123 148 64 158 233 254 34", visualized);
}
@Test
public void testBug1664266() {
//There was an exception and the encoder did not handle the unlatching from
//EDIFACT encoding correctly
String visualized = encodeHighLevel("CREX-TAN:h");
assertEquals("240 13 33 88 181 64 78 124 59 105", visualized);
visualized = encodeHighLevel("CREX-TAN:hh");
assertEquals("240 13 33 88 181 64 78 124 59 105 105 129", visualized);
visualized = encodeHighLevel("CREX-TAN:hhh");
assertEquals("240 13 33 88 181 64 78 124 59 105 105 105", visualized);
}
@Test
public void testBug3048549() {
//There was an IllegalArgumentException for an illegal character here because
//of an encoding problem of the character 0x0060 in Java source code.
String visualized = encodeHighLevel("fiykmj*Rh2`,e6");
assertEquals("239 122 87 154 40 7 171 115 207 12 130 71 155 254 129 237", visualized);
}
@Test
public void testMacroCharacters() {
String visualized = encodeHighLevel("[)>\u001E05\u001D5555\u001C6666\u001E\u0004");
//assertEquals("92 42 63 31 135 30 185 185 29 196 196 31 5 129 87 237", visualized);
assertEquals("236 185 185 29 196 196 129 56", visualized);
}
// Not passing?
/*
@Test
public void testDataURL() {
byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x7E, 0x7F, (byte) 0x80, (byte) 0x81, (byte) 0x82};
String expected = encodeHighLevel(new String(data, Charset.forName("ISO-8859-1")));
String visualized = encodeHighLevel("url(data:text/plain;charset=iso-8859-1,"
+ "%00%01%02%03%04%05%06%07%08%09%0A%7E%7F%80%81%82)");
assertEquals(expected, visualized);
assertEquals("1 2 3 4 5 6 7 8 9 10 11 231 153 173 67 218 112 7", visualized);
visualized = encodeHighLevel("url(data:;base64,flRlc3R+)");
assertEquals("127 85 102 116 117 127 129 56", visualized);
}
*/
private static String encodeHighLevel(String msg) {
String encoded = HighLevelEncoder.encodeHighLevel(msg);
//DecodeHighLevel.decode(encoded);
return visualize(encoded);
}
/**
* Convert a string of char codewords into a different string which lists each character
* using its decimal value.
*
* @param codewords the codewords
* @return the visualized codewords
*/
static String visualize(CharSequence codewords) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < codewords.length(); i++) {
if (i > 0) {
sb.append(' ');
}
sb.append((int) codewords.charAt(i));
}
return sb.toString();
}
}

View file

@ -0,0 +1,63 @@
/*
* 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 org.junit.Assert;
import org.junit.Test;
import java.util.regex.Pattern;
/**
* Tests the DataMatrix placement algorithm.
*/
public final class PlacementTestCase extends Assert {
private static final Pattern SPACE = Pattern.compile(" ");
@Test
public void testPlacement() {
String codewords = unvisualize("66 74 78 66 74 78 129 56 35 102 192 96 226 100 156 1 107 221"); //"AIMAIM" encoded
DebugPlacement placement = new DebugPlacement(codewords, 12, 12);
placement.place();
String[] expected = {
"011100001111",
"001010101000",
"010001010100",
"001010100010",
"000111000100",
"011000010100",
"000100001101",
"011000010000",
"001100001101",
"100010010111",
"011101011010",
"001011001010"};
String[] actual = placement.toBitFieldStringArray();
for (int i = 0; i < actual.length; i++) {
assertEquals("Row " + i, expected[i], actual[i]);
}
}
private static String unvisualize(CharSequence visualized) {
StringBuilder sb = new StringBuilder();
for (String token : SPACE.split(visualized)) {
sb.append((char) Integer.parseInt(token));
}
return sb.toString();
}
}

View file

@ -0,0 +1,108 @@
/*
* 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;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests the SymbolInfo class.
*/
public final class SymbolInfoTestCase extends Assert {
@Test
public void testSymbolInfo() {
SymbolInfo info = SymbolInfo.lookup(3);
assertEquals(5, info.errorCodewords);
assertEquals(8, info.matrixWidth);
assertEquals(8, info.matrixHeight);
assertEquals(10, info.getSymbolWidth());
assertEquals(10, info.getSymbolHeight());
info = SymbolInfo.lookup(3, SymbolShapeHint.FORCE_RECTANGLE);
assertEquals(7, info.errorCodewords);
assertEquals(16, info.matrixWidth);
assertEquals(6, info.matrixHeight);
assertEquals(18, info.getSymbolWidth());
assertEquals(8, info.getSymbolHeight());
info = SymbolInfo.lookup(9);
assertEquals(11, info.errorCodewords);
assertEquals(14, info.matrixWidth);
assertEquals(6, info.matrixHeight);
assertEquals(32, info.getSymbolWidth());
assertEquals(8, info.getSymbolHeight());
info = SymbolInfo.lookup(9, SymbolShapeHint.FORCE_SQUARE);
assertEquals(12, info.errorCodewords);
assertEquals(14, info.matrixWidth);
assertEquals(14, info.matrixHeight);
assertEquals(16, info.getSymbolWidth());
assertEquals(16, info.getSymbolHeight());
try {
SymbolInfo.lookup(1559);
fail("There's no rectangular symbol for more than 1558 data codewords");
} catch (IllegalArgumentException iae) {
//expected
}
try {
SymbolInfo.lookup(50, SymbolShapeHint.FORCE_RECTANGLE);
fail("There's no rectangular symbol for 50 data codewords");
} catch (IllegalArgumentException iae) {
//expected
}
info = SymbolInfo.lookup(35);
assertEquals(24, info.getSymbolWidth());
assertEquals(24, info.getSymbolHeight());
Dimension fixedSize = new Dimension(26, 26);
info = SymbolInfo.lookup(35,
SymbolShapeHint.FORCE_NONE, fixedSize, fixedSize, false);
assertEquals(26, info.getSymbolWidth());
assertEquals(26, info.getSymbolHeight());
info = SymbolInfo.lookup(45,
SymbolShapeHint.FORCE_NONE, fixedSize, fixedSize, false);
assertNull(info);
Dimension minSize = fixedSize;
Dimension maxSize = new Dimension(32, 32);
info = SymbolInfo.lookup(35,
SymbolShapeHint.FORCE_NONE, minSize, maxSize, false);
assertEquals(26, info.getSymbolWidth());
assertEquals(26, info.getSymbolHeight());
info = SymbolInfo.lookup(40,
SymbolShapeHint.FORCE_NONE, minSize, maxSize, false);
assertEquals(26, info.getSymbolWidth());
assertEquals(26, info.getSymbolHeight());
info = SymbolInfo.lookup(45,
SymbolShapeHint.FORCE_NONE, minSize, maxSize, false);
assertEquals(32, info.getSymbolWidth());
assertEquals(32, info.getSymbolHeight());
info = SymbolInfo.lookup(63,
SymbolShapeHint.FORCE_NONE, minSize, maxSize, false);
assertNull(info);
}
}