Added option to force C40 encoding in data matrix (#1495)

* Added option FORCE_C40

* Restored original public API

* Improved javadoc for EncodeHintType.FORCE_C40

* More javadoc fixes
This commit is contained in:
AlexGeller1 2022-01-26 16:47:18 +01:00 committed by GitHub
parent 39440b3015
commit 13465b3f1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 6 deletions

View file

@ -45,7 +45,8 @@ public enum EncodeHintType {
DATA_MATRIX_SHAPE, DATA_MATRIX_SHAPE,
/** /**
* Specifies whether to use compact mode for Data Matrix (type {@link Boolean}, or "true" or "false" * Specifies whether to use compact mode for Data Matrix (type {@link Boolean}, or "true" or "false"
* {@link String } value).
* The compact encoding mode also supports the encoding of characters that are not in the ISO-8859-1 * The compact encoding mode also supports the encoding of characters that are not in the ISO-8859-1
* character set via ECIs. * character set via ECIs.
* Please note that in that case, the most compact character encoding is chosen for characters in * Please note that in that case, the most compact character encoding is chosen for characters in
@ -55,6 +56,7 @@ public enum EncodeHintType {
* Compact encoding also provides GS1-FNC1 support when {@link #GS1_FORMAT} is selected. In this case * Compact encoding also provides GS1-FNC1 support when {@link #GS1_FORMAT} is selected. In this case
* group-separator character (ASCII 29 decimal) can be used to encode the positions of FNC1 codewords * group-separator character (ASCII 29 decimal) can be used to encode the positions of FNC1 codewords
* for the purpose of delimiting AIs. * for the purpose of delimiting AIs.
* This option and {@link #FORCE_C40} are mutually exclusive.
*/ */
DATA_MATRIX_COMPACT, DATA_MATRIX_COMPACT,
@ -127,6 +129,7 @@ public enum EncodeHintType {
/** /**
* Specifies whether to use compact mode for QR code (type {@link Boolean}, or "true" or "false" * Specifies whether to use compact mode for QR code (type {@link Boolean}, or "true" or "false"
* {@link String } value).
* Please note that when compaction is performed, the most compact character encoding is chosen * Please note that when compaction is performed, the most compact character encoding is chosen
* for characters in the input that are not in the ISO-8859-1 character set. Based on experience, * for characters in the input that are not in the ISO-8859-1 character set. Based on experience,
* some scanners do not support encodings like cp-1256 (Arabic). In such cases the encoding can * some scanners do not support encodings like cp-1256 (Arabic). In such cases the encoding can
@ -143,13 +146,21 @@ public enum EncodeHintType {
/** /**
* Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}). * Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}).
* Valid values are "A", "B", "C". * Valid values are "A", "B", "C".
* This option and {@link #CODE128_COMPACT} are mutually exclusive.
*/ */
FORCE_CODE_SET, FORCE_CODE_SET,
/** /**
* Specifies whether to use compact mode for Code-128 code (type {@link Boolean}, or "true" or "false" * Forces C40 encoding for data-matrix (type {@link Boolean}, or "true" or "false") {@link String } value). This
* option and {@link #DATA_MATRIX_COMPACT} are mutually exclusive.
*/
FORCE_C40,
/**
* Specifies whether to use compact mode for Code-128 code (type {@link Boolean}, or "true" or "false"
* {@link String } value).
* This can yield slightly smaller bar codes. This option and {@link #FORCE_CODE_SET} are mutually * This can yield slightly smaller bar codes. This option and {@link #FORCE_CODE_SET} are mutually
* exclusive options. * exclusive.
*/ */
CODE128_COMPACT, CODE128_COMPACT,

View file

@ -99,7 +99,9 @@ public final class DataMatrixWriter implements Writer {
} }
encoded = MinimalEncoder.encodeHighLevel(contents, charset, hasGS1FormatHint ? 0x1D : -1, shape); encoded = MinimalEncoder.encodeHighLevel(contents, charset, hasGS1FormatHint ? 0x1D : -1, shape);
} else { } else {
encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize); 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); SymbolInfo symbolInfo = SymbolInfo.lookup(encoded.length(), shape, minSize, maxSize, true);

View file

@ -23,6 +23,40 @@ class C40Encoder implements Encoder {
return HighLevelEncoder.C40_ENCODATION; 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 @Override
public void encode(EncoderContext context) { public void encode(EncoderContext context) {
//step C //step C

View file

@ -125,7 +125,7 @@ public final class HighLevelEncoder {
* @return the encoded message (the char values range from 0 to 255) * @return the encoded message (the char values range from 0 to 255)
*/ */
public static String encodeHighLevel(String msg) { public static String encodeHighLevel(String msg) {
return encodeHighLevel(msg, SymbolShapeHint.FORCE_NONE, null, null); return encodeHighLevel(msg, SymbolShapeHint.FORCE_NONE, null, null, false);
} }
/** /**
@ -143,9 +143,29 @@ public final class HighLevelEncoder {
SymbolShapeHint shape, SymbolShapeHint shape,
Dimension minSize, Dimension minSize,
Dimension maxSize) { 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 //the codewords 0..255 are encoded as Unicode characters
C40Encoder c40Encoder = new C40Encoder();
Encoder[] encoders = { Encoder[] encoders = {
new ASCIIEncoder(), new C40Encoder(), new TextEncoder(), new ASCIIEncoder(), c40Encoder, new TextEncoder(),
new X12Encoder(), new EdifactEncoder(), new Base256Encoder() new X12Encoder(), new EdifactEncoder(), new Base256Encoder()
}; };
@ -164,6 +184,13 @@ public final class HighLevelEncoder {
} }
int encodingMode = ASCII_ENCODATION; //Default mode int encodingMode = ASCII_ENCODATION; //Default mode
if (forceC40) {
c40Encoder.encodeMaximal(context);
encodingMode = context.getNewEncoding();
context.resetEncoderSignal();
}
while (context.hasMoreCharacters()) { while (context.hasMoreCharacters()) {
encoders[encodingMode].encode(context); encoders[encodingMode].encode(context);
if (context.getNewEncoding() >= 0) { if (context.getNewEncoding() >= 0) {